[Attributor][FIX] Delete all unreachable static functions
authorJohannes Doerfert <johannes@jdoerfert.de>
Thu, 10 Sep 2020 05:03:32 +0000 (00:03 -0500)
committerJohannes Doerfert <johannes@jdoerfert.de>
Wed, 28 Oct 2020 03:07:55 +0000 (22:07 -0500)
Before we used to only mark unreachable static functions as dead if all
uses were known dead. Now we optimistically assume uses to be dead until
proven otherwise.

llvm/include/llvm/Transforms/IPO/Attributor.h
llvm/lib/Transforms/IPO/Attributor.cpp
llvm/test/Transforms/Attributor/ArgumentPromotion/2008-09-08-CGUpdateSelfEdge.ll
llvm/test/Transforms/Attributor/IPConstantProp/deadarg.ll
llvm/test/Transforms/Attributor/liveness.ll

index 17b0916..8467781 100644 (file)
@@ -1574,6 +1574,10 @@ private:
   /// Rewrites function signitures and updates the call graph.
   ChangeStatus cleanupIR();
 
+  /// Identify internal functions that are effectively dead, thus not reachable
+  /// from a live entry point. The functions are added to ToBeDeletedFunctions.
+  void identifyDeadInternalFunctions();
+
   /// Run `::update` on \p AA and track the dependences queried while doing so.
   /// Also adjust the state if we know further updates are not necessary.
   ChangeStatus updateAA(AbstractAttribute &AA);
index 29d860a..6efc220 100644 (file)
@@ -1165,6 +1165,48 @@ ChangeStatus Attributor::manifestAttributes() {
   return ManifestChange;
 }
 
+void Attributor::identifyDeadInternalFunctions() {
+  // Identify dead internal functions and delete them. This happens outside
+  // the other fixpoint analysis as we might treat potentially dead functions
+  // as live to lower the number of iterations. If they happen to be dead, the
+  // below fixpoint loop will identify and eliminate them.
+  SmallVector<Function *, 8> InternalFns;
+  for (Function *F : Functions)
+    if (F->hasLocalLinkage())
+      InternalFns.push_back(F);
+
+  SmallPtrSet<Function *, 8> LiveInternalFns;
+  bool FoundLiveInternal = true;
+  while (FoundLiveInternal) {
+    FoundLiveInternal = false;
+    for (unsigned u = 0, e = InternalFns.size(); u < e; ++u) {
+      Function *F = InternalFns[u];
+      if (!F)
+        continue;
+
+      bool AllCallSitesKnown;
+      if (checkForAllCallSites(
+              [&](AbstractCallSite ACS) {
+                Function *Callee = ACS.getInstruction()->getFunction();
+                return ToBeDeletedFunctions.count(Callee) ||
+                       (Functions.count(Callee) && Callee->hasLocalLinkage() &&
+                        !LiveInternalFns.count(Callee));
+              },
+              *F, true, nullptr, AllCallSitesKnown)) {
+        continue;
+      }
+
+      LiveInternalFns.insert(F);
+      InternalFns[u] = nullptr;
+      FoundLiveInternal = true;
+    }
+  }
+
+  for (unsigned u = 0, e = InternalFns.size(); u < e; ++u)
+    if (Function *F = InternalFns[u])
+      ToBeDeletedFunctions.insert(F);
+}
+
 ChangeStatus Attributor::cleanupIR() {
   TimeTraceScope TimeScope("Attributor::cleanupIR");
   // Delete stuff at the end to avoid invalid references and a nice order.
@@ -1277,43 +1319,14 @@ ChangeStatus Attributor::cleanupIR() {
     DetatchDeadBlocks(ToBeDeletedBBs, nullptr);
   }
 
-  // Identify dead internal functions and delete them. This happens outside
-  // the other fixpoint analysis as we might treat potentially dead functions
-  // as live to lower the number of iterations. If they happen to be dead, the
-  // below fixpoint loop will identify and eliminate them.
-  SmallVector<Function *, 8> InternalFns;
-  for (Function *F : Functions)
-    if (F->hasLocalLinkage())
-      InternalFns.push_back(F);
-
-  bool FoundDeadFn = true;
-  while (FoundDeadFn) {
-    FoundDeadFn = false;
-    for (unsigned u = 0, e = InternalFns.size(); u < e; ++u) {
-      Function *F = InternalFns[u];
-      if (!F)
-        continue;
-
-      bool AllCallSitesKnown;
-      if (!checkForAllCallSites(
-              [this](AbstractCallSite ACS) {
-                return ToBeDeletedFunctions.count(
-                    ACS.getInstruction()->getFunction());
-              },
-              *F, true, nullptr, AllCallSitesKnown))
-        continue;
-
-      ToBeDeletedFunctions.insert(F);
-      InternalFns[u] = nullptr;
-      FoundDeadFn = true;
-    }
-  }
+  identifyDeadInternalFunctions();
 
   // Rewrite the functions as requested during manifest.
   ChangeStatus ManifestChange = rewriteFunctionSignatures(CGModifiedFunctions);
 
   for (Function *Fn : CGModifiedFunctions)
-    CGUpdater.reanalyzeFunction(*Fn);
+    if (!ToBeDeletedFunctions.count(Fn))
+      CGUpdater.reanalyzeFunction(*Fn);
 
   for (Function *Fn : ToBeDeletedFunctions) {
     if (!Functions.count(Fn))
index 19b36c5..e7b2157 100644 (file)
@@ -5,17 +5,6 @@
 ; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal  -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_OPM,IS__CGSCC____,IS________NPM,IS__CGSCC_NPM
 
 define internal fastcc i32 @term_SharingList(i32* %Term, i32* %List) nounwind {
-; IS__TUNIT____: Function Attrs: nounwind
-; IS__TUNIT____-LABEL: define {{[^@]+}}@term_SharingList
-; IS__TUNIT____-SAME: (i32* [[TERM:%.*]], i32* [[LIST:%.*]]) [[ATTR0:#.*]] {
-; IS__TUNIT____-NEXT:  entry:
-; IS__TUNIT____-NEXT:    br i1 false, label [[BB:%.*]], label [[BB5:%.*]]
-; IS__TUNIT____:       bb:
-; IS__TUNIT____-NEXT:    [[TMP0:%.*]] = call fastcc i32 @term_SharingList(i32* null, i32* [[LIST]]) [[ATTR0]]
-; IS__TUNIT____-NEXT:    unreachable
-; IS__TUNIT____:       bb5:
-; IS__TUNIT____-NEXT:    ret i32 0
-;
 ; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn
 ; IS__CGSCC____-LABEL: define {{[^@]+}}@term_SharingList
 ; IS__CGSCC____-SAME: () [[ATTR0:#.*]] {
@@ -40,7 +29,7 @@ bb5:          ; preds = %entry
 define i32 @term_Sharing(i32* %Term) nounwind {
 ; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone willreturn
 ; IS__TUNIT____-LABEL: define {{[^@]+}}@term_Sharing
-; IS__TUNIT____-SAME: (i32* nocapture nofree readnone [[TERM:%.*]]) [[ATTR1:#.*]] {
+; IS__TUNIT____-SAME: (i32* nocapture nofree readnone [[TERM:%.*]]) [[ATTR0:#.*]] {
 ; IS__TUNIT____-NEXT:  entry:
 ; IS__TUNIT____-NEXT:    br i1 false, label [[BB_I:%.*]], label [[BB14:%.*]]
 ; IS__TUNIT____:       bb.i:
index 7cd8585..55c2464 100644 (file)
@@ -4,12 +4,25 @@
 ; RUN: opt -attributor-cgscc -enable-new-pm=0 -attributor-manifest-internal  -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_NPM,IS__CGSCC____,IS________OPM,IS__CGSCC_OPM
 ; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal  -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_OPM,IS__CGSCC____,IS________NPM,IS__CGSCC_NPM
 define internal void @foo(i32 %X) {
-; CHECK-LABEL: define {{[^@]+}}@foo
-; CHECK-SAME: (i32 [[X:%.*]]) {
-; CHECK-NEXT:    call void @foo(i32 [[X]])
-; CHECK-NEXT:    ret void
+; IS__CGSCC____-LABEL: define {{[^@]+}}@foo
+; IS__CGSCC____-SAME: (i32 [[X:%.*]]) {
+; IS__CGSCC____-NEXT:    call void @foo(i32 [[X]])
+; IS__CGSCC____-NEXT:    ret void
 ;
   call void @foo( i32 %X )
   ret void
 }
 
+define void @bar() {
+; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone willreturn
+; IS__TUNIT____-LABEL: define {{[^@]+}}@bar
+; IS__TUNIT____-SAME: () [[ATTR0:#.*]] {
+; IS__TUNIT____-NEXT:    ret void
+;
+; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn
+; IS__CGSCC____-LABEL: define {{[^@]+}}@bar
+; IS__CGSCC____-SAME: () [[ATTR0:#.*]] {
+; IS__CGSCC____-NEXT:    ret void
+;
+  ret void
+}
index e521af8..50c093a 100644 (file)
@@ -705,16 +705,16 @@ cond.end:                                               ; preds = %cond.if, %con
 @a2 = common global i8 0, align 16
 
 define internal i8* @f1(i8* readnone %0) local_unnamed_addr #0 {
-; CHECK-LABEL: define {{[^@]+}}@f1
-; CHECK-SAME: (i8* readnone [[TMP0:%.*]]) local_unnamed_addr {
-; CHECK-NEXT:    [[TMP2:%.*]] = icmp eq i8* [[TMP0]], null
-; CHECK-NEXT:    br i1 [[TMP2]], label [[TMP3:%.*]], label [[TMP5:%.*]]
-; CHECK:       3:
-; CHECK-NEXT:    [[TMP4:%.*]] = tail call i8* @f2(i8* nonnull @a1)
-; CHECK-NEXT:    br label [[TMP5]]
-; CHECK:       5:
-; CHECK-NEXT:    [[TMP6:%.*]] = phi i8* [ [[TMP4]], [[TMP3]] ], [ [[TMP0]], [[TMP1:%.*]] ]
-; CHECK-NEXT:    ret i8* [[TMP6]]
+; IS__CGSCC____-LABEL: define {{[^@]+}}@f1
+; IS__CGSCC____-SAME: (i8* readnone [[TMP0:%.*]]) local_unnamed_addr {
+; IS__CGSCC____-NEXT:    [[TMP2:%.*]] = icmp eq i8* [[TMP0]], null
+; IS__CGSCC____-NEXT:    br i1 [[TMP2]], label [[TMP3:%.*]], label [[TMP5:%.*]]
+; IS__CGSCC____:       3:
+; IS__CGSCC____-NEXT:    [[TMP4:%.*]] = tail call i8* @f2(i8* nonnull @a1)
+; IS__CGSCC____-NEXT:    br label [[TMP5]]
+; IS__CGSCC____:       5:
+; IS__CGSCC____-NEXT:    [[TMP6:%.*]] = phi i8* [ [[TMP4]], [[TMP3]] ], [ [[TMP0]], [[TMP1:%.*]] ]
+; IS__CGSCC____-NEXT:    ret i8* [[TMP6]]
 ;
   %2 = icmp eq i8* %0, null
   br i1 %2, label %3, label %5
@@ -729,19 +729,19 @@ define internal i8* @f1(i8* readnone %0) local_unnamed_addr #0 {
 }
 
 define internal i8* @f2(i8* readnone %0) local_unnamed_addr #0 {
-; CHECK-LABEL: define {{[^@]+}}@f2
-; CHECK-SAME: (i8* readnone [[TMP0:%.*]]) local_unnamed_addr {
-; CHECK-NEXT:    [[TMP2:%.*]] = icmp eq i8* [[TMP0]], null
-; CHECK-NEXT:    br i1 [[TMP2]], label [[TMP5:%.*]], label [[TMP3:%.*]]
-; CHECK:       3:
-; CHECK-NEXT:    [[TMP4:%.*]] = tail call i8* @f1(i8* nonnull [[TMP0]])
-; CHECK-NEXT:    br label [[TMP7:%.*]]
-; CHECK:       5:
-; CHECK-NEXT:    [[TMP6:%.*]] = tail call i8* @f3(i8* nonnull @a2)
-; CHECK-NEXT:    br label [[TMP7]]
-; CHECK:       7:
-; CHECK-NEXT:    [[TMP8:%.*]] = phi i8* [ [[TMP4]], [[TMP3]] ], [ [[TMP6]], [[TMP5]] ]
-; CHECK-NEXT:    ret i8* [[TMP8]]
+; IS__CGSCC____-LABEL: define {{[^@]+}}@f2
+; IS__CGSCC____-SAME: (i8* readnone [[TMP0:%.*]]) local_unnamed_addr {
+; IS__CGSCC____-NEXT:    [[TMP2:%.*]] = icmp eq i8* [[TMP0]], null
+; IS__CGSCC____-NEXT:    br i1 [[TMP2]], label [[TMP5:%.*]], label [[TMP3:%.*]]
+; IS__CGSCC____:       3:
+; IS__CGSCC____-NEXT:    [[TMP4:%.*]] = tail call i8* @f1(i8* nonnull [[TMP0]])
+; IS__CGSCC____-NEXT:    br label [[TMP7:%.*]]
+; IS__CGSCC____:       5:
+; IS__CGSCC____-NEXT:    [[TMP6:%.*]] = tail call i8* @f3(i8* nonnull @a2)
+; IS__CGSCC____-NEXT:    br label [[TMP7]]
+; IS__CGSCC____:       7:
+; IS__CGSCC____-NEXT:    [[TMP8:%.*]] = phi i8* [ [[TMP4]], [[TMP3]] ], [ [[TMP6]], [[TMP5]] ]
+; IS__CGSCC____-NEXT:    ret i8* [[TMP8]]
 ;
   %2 = icmp eq i8* %0, null
   br i1 %2, label %5, label %3
@@ -761,16 +761,16 @@ define internal i8* @f2(i8* readnone %0) local_unnamed_addr #0 {
 }
 
 define internal i8* @f3(i8* readnone %0) local_unnamed_addr #0 {
-; CHECK-LABEL: define {{[^@]+}}@f3
-; CHECK-SAME: (i8* readnone [[TMP0:%.*]]) local_unnamed_addr {
-; CHECK-NEXT:    [[TMP2:%.*]] = icmp eq i8* [[TMP0]], null
-; CHECK-NEXT:    br i1 [[TMP2]], label [[TMP3:%.*]], label [[TMP5:%.*]]
-; CHECK:       3:
-; CHECK-NEXT:    [[TMP4:%.*]] = tail call i8* @f1(i8* nonnull @a2)
-; CHECK-NEXT:    br label [[TMP5]]
-; CHECK:       5:
-; CHECK-NEXT:    [[TMP6:%.*]] = phi i8* [ [[TMP4]], [[TMP3]] ], [ @a1, [[TMP1:%.*]] ]
-; CHECK-NEXT:    ret i8* [[TMP6]]
+; IS__CGSCC____-LABEL: define {{[^@]+}}@f3
+; IS__CGSCC____-SAME: (i8* readnone [[TMP0:%.*]]) local_unnamed_addr {
+; IS__CGSCC____-NEXT:    [[TMP2:%.*]] = icmp eq i8* [[TMP0]], null
+; IS__CGSCC____-NEXT:    br i1 [[TMP2]], label [[TMP3:%.*]], label [[TMP5:%.*]]
+; IS__CGSCC____:       3:
+; IS__CGSCC____-NEXT:    [[TMP4:%.*]] = tail call i8* @f1(i8* nonnull @a2)
+; IS__CGSCC____-NEXT:    br label [[TMP5]]
+; IS__CGSCC____:       5:
+; IS__CGSCC____-NEXT:    [[TMP6:%.*]] = phi i8* [ [[TMP4]], [[TMP3]] ], [ @a1, [[TMP1:%.*]] ]
+; IS__CGSCC____-NEXT:    ret i8* [[TMP6]]
 ;
   %2 = icmp eq i8* %0, null
   br i1 %2, label %3, label %5