[llvm] 6ef4505 - [funcattrs] Infer nosync from readnone and non-convergent

Philip Reames via llvm-commits llvm-commits at lists.llvm.org
Thu Apr 1 11:37:43 PDT 2021


Author: Philip Reames
Date: 2021-04-01T11:37:34-07:00
New Revision: 6ef4505298be08b8cb4243c7d28751e0e315370a

URL: https://github.com/llvm/llvm-project/commit/6ef4505298be08b8cb4243c7d28751e0e315370a
DIFF: https://github.com/llvm/llvm-project/commit/6ef4505298be08b8cb4243c7d28751e0e315370a.diff

LOG: [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

Added: 
    

Modified: 
    llvm/lib/Transforms/IPO/FunctionAttrs.cpp
    llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll
    llvm/test/Other/cgscc-devirt-iteration.ll
    llvm/test/Other/cgscc-iterate-function-mutation.ll
    llvm/test/Other/cgscc-observe-devirt.ll
    llvm/test/Transforms/FunctionAttrs/atomic.ll
    llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll
    llvm/test/Transforms/FunctionAttrs/nofree-attributor.ll
    llvm/test/Transforms/FunctionAttrs/norecurse.ll
    llvm/test/Transforms/FunctionAttrs/nosync.ll
    llvm/test/Transforms/FunctionAttrs/nounwind.ll
    llvm/test/Transforms/FunctionAttrs/optnone.ll
    llvm/test/Transforms/Inline/cgscc-update.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
index 6730824e860ac..87eaf2dd2fdf4 100644
--- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
+++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
@@ -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;
 }
 

diff  --git a/llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll b/llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll
index 5bbc86bb69ed2..d0f142b1cef52 100644
--- a/llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll
+++ b/llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll
@@ -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 }

diff  --git a/llvm/test/Other/cgscc-devirt-iteration.ll b/llvm/test/Other/cgscc-devirt-iteration.ll
index 9a5a2a79d65fb..651b7f3b28539 100644
--- a/llvm/test/Other/cgscc-devirt-iteration.ll
+++ b/llvm/test/Other/cgscc-devirt-iteration.ll
@@ -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 ()*

diff  --git a/llvm/test/Other/cgscc-iterate-function-mutation.ll b/llvm/test/Other/cgscc-iterate-function-mutation.ll
index 6689639311bd0..470f9055ced70 100644
--- 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 @@ exit:
   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
index 3b35f0edc1206..67d630b23a332 100644
--- 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 @@ define void @test2_b3() {
   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
index 313c54b5ed3ef..3208595684fc7 100644
--- a/llvm/test/Transforms/FunctionAttrs/atomic.ll
+++ b/llvm/test/Transforms/FunctionAttrs/atomic.ll
@@ -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 }

diff  --git a/llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll b/llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll
index 4701de3f2e54b..e913aca20c58e 100644
--- a/llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll
+++ b/llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll
@@ -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

diff  --git a/llvm/test/Transforms/FunctionAttrs/nofree-attributor.ll b/llvm/test/Transforms/FunctionAttrs/nofree-attributor.ll
index ef9d086f8f17a..ce33b4c3738cc 100644
--- a/llvm/test/Transforms/FunctionAttrs/nofree-attributor.ll
+++ b/llvm/test/Transforms/FunctionAttrs/nofree-attributor.ll
@@ -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)
 

diff  --git a/llvm/test/Transforms/FunctionAttrs/norecurse.ll b/llvm/test/Transforms/FunctionAttrs/norecurse.ll
index cc48dda663c5e..f5af6406e2a43 100644
--- 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 @@ 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() {

diff  --git a/llvm/test/Transforms/FunctionAttrs/nosync.ll b/llvm/test/Transforms/FunctionAttrs/nosync.ll
index 8d578e6b67fc7..28eb51e9df0cd 100644
--- 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 @@ 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]]

diff  --git a/llvm/test/Transforms/FunctionAttrs/nounwind.ll b/llvm/test/Transforms/FunctionAttrs/nounwind.ll
index 57518f4870cc6..6a667cf73b1e7 100644
--- 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 @@ 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()

diff  --git a/llvm/test/Transforms/FunctionAttrs/optnone.ll b/llvm/test/Transforms/FunctionAttrs/optnone.ll
index 9455a2ae40a01..850142762140a 100644
--- a/llvm/test/Transforms/FunctionAttrs/optnone.ll
+++ b/llvm/test/Transforms/FunctionAttrs/optnone.ll
@@ -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 }

diff  --git a/llvm/test/Transforms/Inline/cgscc-update.ll b/llvm/test/Transforms/Inline/cgscc-update.ll
index b251a5d070c6e..024d57a13d8fc 100644
--- a/llvm/test/Transforms/Inline/cgscc-update.ll
+++ b/llvm/test/Transforms/Inline/cgscc-update.ll
@@ -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:


        


More information about the llvm-commits mailing list