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 @@ -79,6 +79,7 @@ STATISTIC(NumNoUnwind, "Number of functions marked as nounwind"); STATISTIC(NumNoFree, "Number of functions marked as nofree"); STATISTIC(NumWillReturn, "Number of functions marked as willreturn"); +STATISTIC(NumNoSync, "Number of functions marked as nosync"); static cl::opt EnableNonnullArgPropagation( "enable-nonnull-arg-prop", cl::init(true), cl::Hidden, @@ -1472,6 +1473,28 @@ return Changed; } +// Infer the nosync attribute. For the moment, the inference is trivial +// and relies on the readnone attribute already being infered. This will +// be replaced with a more robust implementation in the near future. +static bool addNoSyncAttr(const SCCNodeSet &SCCNodes) { + bool Changed = false; + + for (Function *F : SCCNodes) { + if (!F || F->hasNoSync()) + continue; + + // readnone + not convergent implies nosync + if (!F->doesNotAccessMemory() || F->isConvergent()) + continue; + + F->setNoSync(); + NumNoSync++; + Changed = true; + } + + return Changed; +} + static SCCNodesResult createSCCNodeSet(ArrayRef Functions) { SCCNodesResult Res; Res.HasUnknownCall = false; @@ -1527,6 +1550,8 @@ Changed |= addNoRecurseAttrs(Nodes.SCCNodes); } + Changed |= addNoSyncAttr(Nodes.SCCNodes); + 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,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 nounwind readnone willreturn } +; CHECK: attributes #0 = { norecurse nosync nounwind readnone willreturn } ; CHECK: attributes #1 = { nofree norecurse nounwind willreturn writeonly } ; CHECK: attributes #2 = { nounwind readonly } ; CHECK: attributes #3 = { nounwind } -; CHECK: attributes #4 = { nounwind readnone willreturn } +; CHECK: attributes #4 = { nosync nounwind readnone willreturn } ; CHECK: attributes #5 = { nofree nounwind willreturn } ; CHECK: attributes #6 = { nofree norecurse nounwind willreturn } ; CHECK: attributes #7 = { argmemonly nofree nosync nounwind willreturn } 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: readnone +; AFTER: Function Attrs: 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: readnone +; AFTER2: Function Attrs: 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: readnone +; AFTER2: Function Attrs: 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() readnone +declare void @readnone() nosync readnone declare void @unknown() -declare void @reference_function_pointer(void()*) readnone +declare void @reference_function_pointer(void()*) 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 = { readnone } +; CHECK: attributes #0 = { 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() readnone +declare void @readnone() 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 = { readnone } +; CHECK: attributes #0 = { nosync readnone } 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 nounwind readnone ssp uwtable willreturn } +; CHECK: attributes #0 = { norecurse nosync nounwind readnone ssp uwtable willreturn } ; CHECK: attributes #1 = { nofree norecurse nounwind ssp uwtable willreturn } 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 nounwind readnone willreturn } +; CHECK: attributes #0 = { norecurse nosync nounwind readnone willreturn } ; 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 nounwind readnone uwtable +; FNATTR: Function Attrs: 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 nounwind readnone uwtable +; FNATTR: Function Attrs: 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 nounwind readnone uwtable +; FNATTR: Function Attrs: 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 nounwind readnone uwtable +; FNATTR: Function Attrs: 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 readnone speculatable +; FNATTRS: Function Attrs: 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/norecurse.ll b/llvm/test/Transforms/FunctionAttrs/norecurse.ll --- a/llvm/test/Transforms/FunctionAttrs/norecurse.ll +++ b/llvm/test/Transforms/FunctionAttrs/norecurse.ll @@ -2,7 +2,7 @@ ; RUN: opt < %s -aa-pipeline=basic-aa -passes='cgscc(function-attrs),rpo-function-attrs' -S | FileCheck %s ; CHECK: Function Attrs -; CHECK-SAME: norecurse nounwind readnone +; CHECK-SAME: norecurse nosync nounwind readnone ; CHECK-NEXT: define i32 @leaf() define i32 @leaf() { ret i32 1 @@ -61,7 +61,7 @@ declare void @llvm.memcpy.p0i8.p0i8.i32(i8*, i8*, i32, i1) ; CHECK: Function Attrs -; CHECK-SAME: norecurse readnone +; CHECK-SAME: norecurse nosync readnone ; FIXME: missing "norecurse" ; CHECK-NEXT: define internal i32 @called_by_norecurse() define internal i32 @called_by_norecurse() { @@ -76,7 +76,7 @@ } ; CHECK: Function Attrs -; CHECK-SAME: norecurse readnone +; CHECK-SAME: norecurse nosync readnone ; FIXME: missing "norecurse" ; CHECK-NEXT: define internal i32 @called_by_norecurse_indirectly() define internal i32 @called_by_norecurse_indirectly() { 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 @@ -4,7 +4,7 @@ ; Base case, empty function define void @test1() { -; CHECK: Function Attrs: norecurse nounwind readnone willreturn +; CHECK: Function Attrs: norecurse nosync nounwind readnone willreturn ; CHECK-LABEL: @test1( ; CHECK-NEXT: ret void ; @@ -13,7 +13,7 @@ ; Show the bottom up walk define void @test2() { -; CHECK: Function Attrs: norecurse nounwind readnone willreturn +; CHECK: Function Attrs: norecurse nosync nounwind readnone willreturn ; CHECK-LABEL: @test2( ; CHECK-NEXT: call void @test1() ; CHECK-NEXT: ret void @@ -36,7 +36,7 @@ } define i32 @test4(i32 %a, i32 %b) { -; CHECK: Function Attrs: norecurse nounwind readnone willreturn +; CHECK: Function Attrs: norecurse nosync nounwind readnone willreturn ; CHECK-LABEL: @test4( ; CHECK-NEXT: [[ADD:%.*]] = add i32 [[A:%.*]], [[B:%.*]] ; CHECK-NEXT: ret i32 [[A]] 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 nounwind readnone +; CHECK: Function Attrs: norecurse nosync nounwind readnone ; CHECK-NEXT: define i32 @foo1() define i32 @foo1() { ret i32 1 } ; TEST 2 -; CHECK: Function Attrs: nounwind readnone +; CHECK: Function Attrs: 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: nounwind readnone +; CHECK: Function Attrs: 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 nounwind readnone willreturn } +; CHECK: = { norecurse nosync nounwind readnone willreturn } ; CHECK-LABEL: attributes #1 ; CHECK: = { noinline optnone } 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 nounwind readnone +; CHECK: Function Attrs: 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 nounwind readnone +; CHECK: Function Attrs: 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 nounwind readnone +; CHECK: Function Attrs: 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 nounwind readnone +; CHECK: Function Attrs: 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 nounwind readnone +; CHECK: Function Attrs: 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 nounwind readnone +; CHECK: Function Attrs: noinline nosync nounwind readnone ; CHECK-NEXT: define void @test4_h() define void @test4_h() noinline { entry: