1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2025-01-31 20:51:52 +01:00

[funcattrs] Infer nosync from readnone and non-convergent

This implements the most basic possible nosync inference. The choice of inference rule is taken from the comments in attributor and the discussion on the review of the change which introduced the nosync attribute (0626367202c).

This is deliberately minimal. As noted in code comments, I do plan to add a more robust inference which actually scans the function IR directly, but a) I need to do some refactoring of the attributor code to use common interfaces, and b) I wanted to get something in. I also wanted to minimize the "interesting" analysis discussion since that's time intensive.

Context: This combines with existing nofree attribute inference to help prove dereferenceability in the ongoing deref-at-point semantics work.

Differential Revision: https://reviews.llvm.org/D99749
This commit is contained in:
Philip Reames 2021-04-01 11:34:43 -07:00
parent 5615539472
commit db585a3ff9
13 changed files with 58 additions and 33 deletions

View File

@ -79,6 +79,7 @@ STATISTIC(NumNoRecurse, "Number of functions marked as norecurse");
STATISTIC(NumNoUnwind, "Number of functions marked as nounwind"); STATISTIC(NumNoUnwind, "Number of functions marked as nounwind");
STATISTIC(NumNoFree, "Number of functions marked as nofree"); STATISTIC(NumNoFree, "Number of functions marked as nofree");
STATISTIC(NumWillReturn, "Number of functions marked as willreturn"); STATISTIC(NumWillReturn, "Number of functions marked as willreturn");
STATISTIC(NumNoSync, "Number of functions marked as nosync");
static cl::opt<bool> EnableNonnullArgPropagation( static cl::opt<bool> EnableNonnullArgPropagation(
"enable-nonnull-arg-prop", cl::init(true), cl::Hidden, "enable-nonnull-arg-prop", cl::init(true), cl::Hidden,
@ -1472,6 +1473,28 @@ static bool addWillReturn(const SCCNodeSet &SCCNodes) {
return Changed; 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<Function *> Functions) { static SCCNodesResult createSCCNodeSet(ArrayRef<Function *> Functions) {
SCCNodesResult Res; SCCNodesResult Res;
Res.HasUnknownCall = false; Res.HasUnknownCall = false;
@ -1527,6 +1550,8 @@ static bool deriveAttrsInPostOrder(ArrayRef<Function *> Functions,
Changed |= addNoRecurseAttrs(Nodes.SCCNodes); Changed |= addNoRecurseAttrs(Nodes.SCCNodes);
} }
Changed |= addNoSyncAttr(Nodes.SCCNodes);
return Changed; return Changed;
} }

View File

@ -72,11 +72,11 @@ define i32 @test3_no(i8* %p) nounwind {
declare void @callee(i32* %p) nounwind declare void @callee(i32* %p) nounwind
declare void @llvm.memcpy.p0i8.p0i8.i64(i8*, i8*, i64, i1) 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 #1 = { nofree norecurse nounwind willreturn writeonly }
; CHECK: attributes #2 = { nounwind readonly } ; CHECK: attributes #2 = { nounwind readonly }
; CHECK: attributes #3 = { nounwind } ; 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 #5 = { nofree nounwind willreturn }
; CHECK: attributes #6 = { nofree norecurse nounwind willreturn } ; CHECK: attributes #6 = { nofree norecurse nounwind willreturn }
; CHECK: attributes #7 = { argmemonly nofree nosync nounwind willreturn } ; CHECK: attributes #7 = { argmemonly nofree nosync nounwind willreturn }

View File

@ -28,7 +28,7 @@ declare void @unknown()
define void @test1() { define void @test1() {
; BEFORE-NOT: Function Attrs ; BEFORE-NOT: Function Attrs
; AFTER: Function Attrs: readnone ; AFTER: Function Attrs: nosync readnone
; CHECK-LABEL: define void @test1() ; CHECK-LABEL: define void @test1()
entry: entry:
%fptr = alloca void ()* %fptr = alloca void ()*
@ -57,7 +57,7 @@ declare void @readnone_with_arg(void ()**) readnone
define void @test2_a(void ()** %ignore) { define void @test2_a(void ()** %ignore) {
; BEFORE-NOT: Function Attrs ; BEFORE-NOT: Function Attrs
; AFTER1: Function Attrs: readonly ; AFTER1: Function Attrs: readonly
; AFTER2: Function Attrs: readnone ; AFTER2: Function Attrs: nosync readnone
; BEFORE: define void @test2_a(void ()** %ignore) ; BEFORE: define void @test2_a(void ()** %ignore)
; AFTER: define void @test2_a(void ()** readnone %ignore) ; AFTER: define void @test2_a(void ()** readnone %ignore)
entry: entry:
@ -78,7 +78,7 @@ entry:
define void @test2_b() { define void @test2_b() {
; BEFORE-NOT: Function Attrs ; BEFORE-NOT: Function Attrs
; AFTER1: Function Attrs: readonly ; AFTER1: Function Attrs: readonly
; AFTER2: Function Attrs: readnone ; AFTER2: Function Attrs: nosync readnone
; CHECK-LABEL: define void @test2_b() ; CHECK-LABEL: define void @test2_b()
entry: entry:
%f2ptr = alloca void ()* %f2ptr = alloca void ()*

View File

@ -1,8 +1,8 @@
; RUN: opt -aa-pipeline=basic-aa -passes='cgscc(function-attrs,function(simplify-cfg))' -S < %s | FileCheck %s ; 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 @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 ; 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 ; simplify-cfg to delete call edges and this ends up splitting both the SCCs
@ -338,4 +338,4 @@ exit:
ret void ret void
} }
; CHECK: attributes #0 = { readnone } ; CHECK: attributes #0 = { nosync readnone }

View File

@ -10,7 +10,7 @@
; without requiring the outer manager to iterate doesn't break any invariant. ; 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 ; 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() declare void @unknown()
; The @test1_* checks that if we refine an indirect call to a direct call and ; The @test1_* checks that if we refine an indirect call to a direct call and
@ -103,4 +103,4 @@ define void @test2_b3() {
ret void ret void
} }
; CHECK: attributes #0 = { readnone } ; CHECK: attributes #0 = { nosync readnone }

View File

@ -20,5 +20,5 @@ entry:
ret i32 %r 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 } ; CHECK: attributes #1 = { nofree norecurse nounwind ssp uwtable willreturn }

View File

@ -28,5 +28,5 @@ entry:
attributes #0 = { argmemonly } attributes #0 = { argmemonly }
attributes #1 = { inaccessiblememonly } attributes #1 = { inaccessiblememonly }
attributes #2 = { inaccessiblemem_or_argmemonly } attributes #2 = { inaccessiblemem_or_argmemonly }
; CHECK: attributes #0 = { norecurse nounwind readnone willreturn } ; CHECK: attributes #0 = { norecurse nosync nounwind readnone willreturn }
; CHECK-NOT: attributes ; CHECK-NOT: attributes

View File

@ -12,7 +12,7 @@ declare void @_ZdaPv(i8*) local_unnamed_addr #2
; TEST 1 (positive case) ; 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() ; FNATTR-NEXT: define void @only_return()
define void @only_return() #0 { define void @only_return() #0 {
ret void ret void
@ -78,14 +78,14 @@ end:
; } ; }
; FNATTR: Function Attrs: noinline nounwind readnone uwtable ; FNATTR: Function Attrs: noinline nosync nounwind readnone uwtable
; FNATTR-NEXT: define void @mutual_recursion1() ; FNATTR-NEXT: define void @mutual_recursion1()
define void @mutual_recursion1() #0 { define void @mutual_recursion1() #0 {
call void @mutual_recursion2() call void @mutual_recursion2()
ret void ret void
} }
; FNATTR: Function Attrs: noinline nounwind readnone uwtable ; FNATTR: Function Attrs: noinline nosync nounwind readnone uwtable
; FNATTR-NEXT: define void @mutual_recursion2() ; FNATTR-NEXT: define void @mutual_recursion2()
define void @mutual_recursion2() #0 { define void @mutual_recursion2() #0 {
call void @mutual_recursion1() call void @mutual_recursion1()
@ -132,7 +132,7 @@ define noalias i8* @call_realloc(i8* nocapture %0, i64 %1) local_unnamed_addr #0
; FNATTR-NEXT: declare void @nofree_function() ; FNATTR-NEXT: declare void @nofree_function()
declare void @nofree_function() nofree readnone #0 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() ; FNATTR-NEXT: define void @call_nofree_function()
define void @call_nofree_function() #0 { define void @call_nofree_function() #0 {
tail call void @nofree_function() tail call void @nofree_function()
@ -168,7 +168,7 @@ define void @call_both() #0 {
; TEST 10 (positive case) ; TEST 10 (positive case)
; Call intrinsic function ; 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) ; FNATTRS-NEXT: declare float @llvm.floor.f32(float %0)
declare float @llvm.floor.f32(float) declare float @llvm.floor.f32(float)

View File

@ -2,7 +2,7 @@
; RUN: opt < %s -aa-pipeline=basic-aa -passes='cgscc(function-attrs),rpo-function-attrs' -S | FileCheck %s ; RUN: opt < %s -aa-pipeline=basic-aa -passes='cgscc(function-attrs),rpo-function-attrs' -S | FileCheck %s
; CHECK: Function Attrs ; CHECK: Function Attrs
; CHECK-SAME: norecurse nounwind readnone ; CHECK-SAME: norecurse nosync nounwind readnone
; CHECK-NEXT: define i32 @leaf() ; CHECK-NEXT: define i32 @leaf()
define i32 @leaf() { define i32 @leaf() {
ret i32 1 ret i32 1
@ -61,7 +61,7 @@ define void @intrinsic(i8* %dest, i8* %src, i32 %len) {
declare void @llvm.memcpy.p0i8.p0i8.i32(i8*, i8*, i32, i1) declare void @llvm.memcpy.p0i8.p0i8.i32(i8*, i8*, i32, i1)
; CHECK: Function Attrs ; CHECK: Function Attrs
; CHECK-SAME: norecurse readnone ; CHECK-SAME: norecurse nosync readnone
; FIXME: missing "norecurse" ; FIXME: missing "norecurse"
; CHECK-NEXT: define internal i32 @called_by_norecurse() ; CHECK-NEXT: define internal i32 @called_by_norecurse()
define internal i32 @called_by_norecurse() { define internal i32 @called_by_norecurse() {
@ -76,7 +76,7 @@ define void @m() norecurse {
} }
; CHECK: Function Attrs ; CHECK: Function Attrs
; CHECK-SAME: norecurse readnone ; CHECK-SAME: norecurse nosync readnone
; FIXME: missing "norecurse" ; FIXME: missing "norecurse"
; CHECK-NEXT: define internal i32 @called_by_norecurse_indirectly() ; CHECK-NEXT: define internal i32 @called_by_norecurse_indirectly()
define internal i32 @called_by_norecurse_indirectly() { define internal i32 @called_by_norecurse_indirectly() {

View File

@ -4,7 +4,7 @@
; Base case, empty function ; Base case, empty function
define void @test1() { define void @test1() {
; CHECK: Function Attrs: norecurse nounwind readnone willreturn ; CHECK: Function Attrs: norecurse nosync nounwind readnone willreturn
; CHECK-LABEL: @test1( ; CHECK-LABEL: @test1(
; CHECK-NEXT: ret void ; CHECK-NEXT: ret void
; ;
@ -13,7 +13,7 @@ define void @test1() {
; Show the bottom up walk ; Show the bottom up walk
define void @test2() { define void @test2() {
; CHECK: Function Attrs: norecurse nounwind readnone willreturn ; CHECK: Function Attrs: norecurse nosync nounwind readnone willreturn
; CHECK-LABEL: @test2( ; CHECK-LABEL: @test2(
; CHECK-NEXT: call void @test1() ; CHECK-NEXT: call void @test1()
; CHECK-NEXT: ret void ; CHECK-NEXT: ret void
@ -36,7 +36,7 @@ define void @test3() convergent {
} }
define i32 @test4(i32 %a, i32 %b) { 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-LABEL: @test4(
; CHECK-NEXT: [[ADD:%.*]] = add i32 [[A:%.*]], [[B:%.*]] ; CHECK-NEXT: [[ADD:%.*]] = add i32 [[A:%.*]], [[B:%.*]]
; CHECK-NEXT: ret i32 [[A]] ; CHECK-NEXT: ret i32 [[A]]

View File

@ -1,14 +1,14 @@
; RUN: opt < %s -function-attrs -S | FileCheck %s ; RUN: opt < %s -function-attrs -S | FileCheck %s
; TEST 1 ; TEST 1
; CHECK: Function Attrs: norecurse nounwind readnone ; CHECK: Function Attrs: norecurse nosync nounwind readnone
; CHECK-NEXT: define i32 @foo1() ; CHECK-NEXT: define i32 @foo1()
define i32 @foo1() { define i32 @foo1() {
ret i32 1 ret i32 1
} }
; TEST 2 ; TEST 2
; CHECK: Function Attrs: nounwind readnone ; CHECK: Function Attrs: nosync nounwind readnone
; CHECK-NEXT: define i32 @scc1_foo() ; CHECK-NEXT: define i32 @scc1_foo()
define i32 @scc1_foo() { define i32 @scc1_foo() {
%1 = call i32 @scc1_bar() %1 = call i32 @scc1_bar()
@ -17,7 +17,7 @@ define i32 @scc1_foo() {
; TEST 3 ; TEST 3
; CHECK: Function Attrs: nounwind readnone ; CHECK: Function Attrs: nosync nounwind readnone
; CHECK-NEXT: define i32 @scc1_bar() ; CHECK-NEXT: define i32 @scc1_bar()
define i32 @scc1_bar() { define i32 @scc1_bar() {
%1 = call i32 @scc1_foo() %1 = call i32 @scc1_foo()

View File

@ -20,6 +20,6 @@ declare i8 @strlen(i8*) noinline optnone
; CHECK: (i8*) #1 ; CHECK: (i8*) #1
; CHECK-LABEL: attributes #0 ; CHECK-LABEL: attributes #0
; CHECK: = { norecurse nounwind readnone willreturn } ; CHECK: = { norecurse nosync nounwind readnone willreturn }
; CHECK-LABEL: attributes #1 ; CHECK-LABEL: attributes #1
; CHECK: = { noinline optnone } ; CHECK: = { noinline optnone }

View File

@ -27,7 +27,7 @@ entry:
} }
; This function should have had 'readnone' deduced for its SCC. ; 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() ; CHECK-NEXT: define void @test1_g()
define void @test1_g() noinline { define void @test1_g() noinline {
entry: entry:
@ -36,7 +36,7 @@ entry:
} }
; This function should have had 'readnone' deduced for its SCC. ; 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() ; CHECK-NEXT: define void @test1_h()
define void @test1_h() noinline { define void @test1_h() noinline {
entry: entry:
@ -59,7 +59,7 @@ entry:
} }
; This function should have had 'readnone' deduced for its SCC. ; 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() ; CHECK-NEXT: define void @test2_g()
define void @test2_g() noinline { define void @test2_g() noinline {
entry: entry:
@ -69,7 +69,7 @@ entry:
} }
; This function should have had 'readnone' deduced for its SCC. ; 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() ; CHECK-NEXT: define void @test2_h()
define void @test2_h() noinline { define void @test2_h() noinline {
entry: entry:
@ -152,7 +152,7 @@ exit:
; form a new SCC and should use that can deduce precise function attrs. ; form a new SCC and should use that can deduce precise function attrs.
; This function should have had 'readnone' deduced for its SCC. ; 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() ; CHECK-NEXT: define void @test4_f1()
define void @test4_f1() noinline { define void @test4_f1() noinline {
entry: entry:
@ -175,7 +175,7 @@ entry:
} }
; This function should have had 'readnone' deduced for its SCC. ; 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() ; CHECK-NEXT: define void @test4_h()
define void @test4_h() noinline { define void @test4_h() noinline {
entry: entry: