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:
parent
5615539472
commit
db585a3ff9
@ -79,6 +79,7 @@ STATISTIC(NumNoRecurse, "Number of functions marked as norecurse");
|
||||
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<bool> EnableNonnullArgPropagation(
|
||||
"enable-nonnull-arg-prop", cl::init(true), cl::Hidden,
|
||||
@ -1472,6 +1473,28 @@ static bool addWillReturn(const SCCNodeSet &SCCNodes) {
|
||||
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) {
|
||||
SCCNodesResult Res;
|
||||
Res.HasUnknownCall = false;
|
||||
@ -1527,6 +1550,8 @@ static bool deriveAttrsInPostOrder(ArrayRef<Function *> Functions,
|
||||
Changed |= addNoRecurseAttrs(Nodes.SCCNodes);
|
||||
}
|
||||
|
||||
Changed |= addNoSyncAttr(Nodes.SCCNodes);
|
||||
|
||||
return Changed;
|
||||
}
|
||||
|
||||
|
@ -72,11 +72,11 @@ define i32 @test3_no(i8* %p) nounwind {
|
||||
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 }
|
||||
|
@ -28,7 +28,7 @@ declare void @unknown()
|
||||
|
||||
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 @@ declare void @readnone_with_arg(void ()**) readnone
|
||||
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 @@ entry:
|
||||
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 ()*
|
||||
|
@ -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 @@ exit:
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK: attributes #0 = { readnone }
|
||||
; CHECK: attributes #0 = { nosync readnone }
|
||||
|
@ -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 @@ define void @test2_b3() {
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK: attributes #0 = { readnone }
|
||||
; CHECK: attributes #0 = { nosync readnone }
|
||||
|
@ -20,5 +20,5 @@ entry:
|
||||
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 }
|
||||
|
@ -28,5 +28,5 @@ entry:
|
||||
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
|
||||
|
@ -12,7 +12,7 @@ declare void @_ZdaPv(i8*) local_unnamed_addr #2
|
||||
|
||||
|
||||
; 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 @@ end:
|
||||
; }
|
||||
|
||||
|
||||
; 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 @@ define noalias i8* @call_realloc(i8* nocapture %0, i64 %1) local_unnamed_addr #0
|
||||
; 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 @@ define void @call_both() #0 {
|
||||
|
||||
; 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)
|
||||
|
||||
|
@ -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 @@ define void @intrinsic(i8* %dest, i8* %src, i32 %len) {
|
||||
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 @@ define void @m() norecurse {
|
||||
}
|
||||
|
||||
; 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() {
|
||||
|
@ -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 @@ define void @test1() {
|
||||
|
||||
; 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 void @test3() convergent {
|
||||
}
|
||||
|
||||
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]]
|
||||
|
@ -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 @@ define i32 @scc1_foo() {
|
||||
|
||||
|
||||
; 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()
|
||||
|
@ -20,6 +20,6 @@ declare i8 @strlen(i8*) noinline optnone
|
||||
; 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 }
|
||||
|
@ -27,7 +27,7 @@ entry:
|
||||
}
|
||||
|
||||
; 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 @@ entry:
|
||||
}
|
||||
|
||||
; 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 @@ entry:
|
||||
}
|
||||
|
||||
; 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 @@ entry:
|
||||
}
|
||||
|
||||
; 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 @@ exit:
|
||||
; 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 @@ entry:
|
||||
}
|
||||
|
||||
; 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:
|
||||
|
Loading…
x
Reference in New Issue
Block a user