[Inliner] Don't removeDeadConstantUsers() when checking if a function is dead
authorArthur Eubanks <aeubanks@google.com>
Thu, 13 Jan 2022 18:09:52 +0000 (10:09 -0800)
committerArthur Eubanks <aeubanks@google.com>
Thu, 13 Jan 2022 22:29:45 +0000 (14:29 -0800)
If a function has many uses, this can take a good chunk of compile times.

Reviewed By: nikic

Differential Revision: https://reviews.llvm.org/D117236

llvm/include/llvm/IR/Constant.h
llvm/lib/Analysis/LazyCallGraph.cpp
llvm/lib/IR/Constants.cpp
llvm/lib/Transforms/IPO/Inliner.cpp

index c8999b7..a97372e 100644 (file)
@@ -204,6 +204,12 @@ public:
   /// Constant::removeDeadConstantUsers, but doesn't remove dead constants.
   bool hasOneLiveUse() const;
 
+  /// Return true if the constant has no live uses.
+  ///
+  /// This returns the same result as calling Value::use_empty after
+  /// Constant::removeDeadConstantUsers, but doesn't remove dead constants.
+  bool hasZeroLiveUses() const;
+
   const Constant *stripPointerCasts() const {
     return cast<Constant>(Value::stripPointerCasts());
   }
@@ -244,6 +250,8 @@ private:
 
   /// Determine what potential relocations may be needed by this constant.
   PossibleRelocationsTy getRelocationInfo() const;
+
+  bool hasNLiveUses(unsigned N) const;
 };
 
 } // end namespace llvm
index ecac2d2..e8e9593 100644 (file)
@@ -1503,7 +1503,7 @@ void LazyCallGraph::removeEdge(Node &SourceN, Node &TargetN) {
 void LazyCallGraph::removeDeadFunction(Function &F) {
   // FIXME: This is unnecessarily restrictive. We should be able to remove
   // functions which recursively call themselves.
-  assert(F.use_empty() &&
+  assert(F.hasZeroLiveUses() &&
          "This routine should only be called on trivially dead functions!");
 
   // We shouldn't remove library functions as they are never really dead while
index e753fc7..8fb17f6 100644 (file)
@@ -779,18 +779,22 @@ void Constant::removeDeadConstantUsers() const {
   }
 }
 
-bool Constant::hasOneLiveUse() const {
+bool Constant::hasOneLiveUse() const { return hasNLiveUses(1); }
+
+bool Constant::hasZeroLiveUses() const { return hasNLiveUses(0); }
+
+bool Constant::hasNLiveUses(unsigned N) const {
   unsigned NumUses = 0;
-  for (const Use &use : uses()) {
-    const Constant *User = dyn_cast<Constant>(use.getUser());
+  for (const Use &U : uses()) {
+    const Constant *User = dyn_cast<Constant>(U.getUser());
     if (!User || !constantIsDead(User, /* RemoveDeadUsers= */ false)) {
       ++NumUses;
 
-      if (NumUses > 1)
+      if (NumUses > N)
         return false;
     }
   }
-  return NumUses == 1;
+  return NumUses == N;
 }
 
 Constant *Constant::replaceUndefsWith(Constant *C, Constant *Replacement) {
index c5527c7..fa264fd 100644 (file)
@@ -940,24 +940,20 @@ PreservedAnalyses InlinerPass::run(LazyCallGraph::SCC &InitialC,
       // which may reduce the number of callers of other functions to one,
       // changing inline cost thresholds.
       bool CalleeWasDeleted = false;
-      if (Callee.hasLocalLinkage()) {
-        // To check this we also need to nuke any dead constant uses (perhaps
-        // made dead by this operation on other functions).
-        Callee.removeDeadConstantUsers();
-        if (Callee.use_empty() && !CG.isLibFunction(Callee)) {
-          Calls->erase_if([&](const std::pair<CallBase *, int> &Call) {
-            return Call.first->getCaller() == &Callee;
-          });
-          // Clear the body and queue the function itself for deletion when we
-          // finish inlining and call graph updates.
-          // Note that after this point, it is an error to do anything other
-          // than use the callee's address or delete it.
-          Callee.dropAllReferences();
-          assert(!is_contained(DeadFunctions, &Callee) &&
-                 "Cannot put cause a function to become dead twice!");
-          DeadFunctions.push_back(&Callee);
-          CalleeWasDeleted = true;
-        }
+      if (Callee.hasLocalLinkage() && Callee.hasZeroLiveUses() &&
+          !CG.isLibFunction(Callee)) {
+        Calls->erase_if([&](const std::pair<CallBase *, int> &Call) {
+          return Call.first->getCaller() == &Callee;
+        });
+        // Clear the body and queue the function itself for deletion when we
+        // finish inlining and call graph updates.
+        // Note that after this point, it is an error to do anything other
+        // than use the callee's address or delete it.
+        Callee.dropAllReferences();
+        assert(!is_contained(DeadFunctions, &Callee) &&
+               "Cannot put cause a function to become dead twice!");
+        DeadFunctions.push_back(&Callee);
+        CalleeWasDeleted = true;
       }
       if (CalleeWasDeleted)
         Advice->recordInliningWithCalleeDeleted();