This is effectively a complete rewrite of the globaldce algorithm, resulting
authorChris Lattner <sabre@nondot.org>
Tue, 16 Sep 2003 19:27:31 +0000 (19:27 +0000)
committerChris Lattner <sabre@nondot.org>
Tue, 16 Sep 2003 19:27:31 +0000 (19:27 +0000)
in it being both shorter and more effective.  It no longer depends on the
callgraph, so one FIXME has been fixed.

Additionally, this pass was not able to delete recursive (but dead) functions
if they were pointed to by global variables which were also dead.  In fact
this pass had a lot of problems deleting functions which were only pointed
to by dead globals and other stuff.

Fixing this means that the entire EH library should be stripped away now from
programs that don't use sjlj or exceptions.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@8567 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Transforms/IPO/GlobalDCE.cpp

index 5ce79625b84b72b9a3ec384ed4e59f55c4b0da88..2e18c43bac37c34ff253961d32dd71af1aae8832 100644 (file)
@@ -1,93 +1,39 @@
 //===-- GlobalDCE.cpp - DCE unreachable internal functions ----------------===//
 //
-// This transform is designed to eliminate unreachable internal globals
-// FIXME: GlobalDCE should update the callgraph, not destroy it!
+// This transform is designed to eliminate unreachable internal globals from the
+// program.  It uses an aggressive algorithm, searching out globals that are
+// known to be alive.  After it finds all of the globals which are needed, it
+// deletes whatever is left over.  This allows it to delete recursive chunks of
+// the program which are unreachable.
 //
 //===----------------------------------------------------------------------===//
 
 #include "llvm/Transforms/IPO.h"
-#include "llvm/Module.h"
 #include "llvm/Constants.h"
-#include "llvm/DerivedTypes.h"
-#include "llvm/Analysis/CallGraph.h"
-#include "Support/DepthFirstIterator.h"
+#include "llvm/Module.h"
+#include "llvm/Pass.h"
 #include "Support/Statistic.h"
-#include <algorithm>
+#include <set>
 
 namespace {
   Statistic<> NumFunctions("globaldce","Number of functions removed");
   Statistic<> NumVariables("globaldce","Number of global variables removed");
   Statistic<> NumCPRs("globaldce", "Number of const pointer refs removed");
-  Statistic<> NumConsts("globaldce", "Number of init constants removed");
-
-  bool RemoveUnreachableFunctions(Module &M, CallGraph &CallGraph) {
-    // Calculate which functions are reachable from the external functions in
-    // the call graph.
-    //
-    std::set<CallGraphNode*> ReachableNodes(df_begin(&CallGraph),
-                                            df_end(&CallGraph));
-
-    // Loop over the functions in the module twice.  The first time is used to
-    // drop references that functions have to each other before they are
-    // deleted.  The second pass removes the functions that need to be removed.
-    //
-    std::vector<CallGraphNode*> FunctionsToDelete;   // Track unused functions
-    for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) {
-      CallGraphNode *N = CallGraph[I];
-      
-      if (!ReachableNodes.count(N)) {              // Not reachable??
-        I->dropAllReferences();
-        N->removeAllCalledFunctions();
-        FunctionsToDelete.push_back(N);
-        ++NumFunctions;
-      }
-    }
-    
-    // Nothing to do if no unreachable functions have been found...
-    if (FunctionsToDelete.empty()) return false;
-    
-    // Unreachable functions have been found and should have no references to
-    // them, delete them now.
-    //
-    for (std::vector<CallGraphNode*>::iterator I = FunctionsToDelete.begin(),
-           E = FunctionsToDelete.end(); I != E; ++I)
-      delete CallGraph.removeFunctionFromModule(*I);
-
-    // Walk the function list, removing prototypes for functions which are not
-    // used.
-    for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I)
-      if (I->use_size() == 0 && I->isExternal()) {
-        CallGraph[I]->removeAllCalledFunctions();
-        delete CallGraph.removeFunctionFromModule(I);
-      }
 
-    return true;
-  }
-  
   struct GlobalDCE : public Pass {
     // run - Do the GlobalDCE pass on the specified module, optionally updating
     // the specified callgraph to reflect the changes.
     //
-    bool run(Module &M) {
-      return RemoveUnreachableFunctions(M, getAnalysis<CallGraph>()) |
-             RemoveUnreachableGlobalVariables(M);
-    }
-
-    // getAnalysisUsage - This function works on the call graph of a module.
-    // It is capable of updating the call graph to reflect the new state of the
-    // module.
-    //
-    virtual void getAnalysisUsage(AnalysisUsage &AU) const {
-      AU.addRequired<CallGraph>();
-    }
+    bool run(Module &M);
 
   private:
-    std::vector<GlobalValue*> WorkList;
+    std::set<GlobalValue*> AliveGlobals;
 
-    inline bool RemoveIfDead(GlobalValue *GV);
-    void DestroyInitializer(Constant *C);
+    /// MarkGlobalIsNeeded - the specific global value as needed, and
+    /// recursively mark anything that it uses as also needed.
+    void GlobalIsNeeded(GlobalValue *GV);
+    void MarkUsedGlobalsAsNeeded(Constant *C);
 
-    bool RemoveUnreachableGlobalVariables(Module &M);
     bool RemoveUnusedConstantPointerRef(GlobalValue &GV);
     bool SafeToDestroyConstant(Constant *C);
   };
@@ -96,135 +42,116 @@ namespace {
 
 Pass *createGlobalDCEPass() { return new GlobalDCE(); }
 
+bool GlobalDCE::run(Module &M) {
+  bool Changed = false;
+  // Loop over the module, adding globals which are obviously necessary.
+  for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) {
+    Changed |= RemoveUnusedConstantPointerRef(*I);
+    // Functions with external linkage are needed if they have a body
+    if (I->hasExternalLinkage() && !I->isExternal())
+      GlobalIsNeeded(I);
+  }
 
-// RemoveIfDead - If this global value is dead, remove it from the current
-// module and return true.
-//
-bool GlobalDCE::RemoveIfDead(GlobalValue *GV) {
-  // If there is only one use of the global value, it might be a
-  // ConstantPointerRef... which means that this global might actually be
-  // dead.
-  if (GV->use_size() == 1)
-    RemoveUnusedConstantPointerRef(*GV);
-
-  if (!GV->use_empty()) return false;
-
-  if (GlobalVariable *GVar = dyn_cast<GlobalVariable>(GV)) {
-    // Eliminate all global variables that are unused, and that are internal, or
-    // do not have an initializer.
-    //
-    if (GVar->hasInternalLinkage() || GVar->isExternal()) {
-      Constant *Init = GVar->hasInitializer() ? GVar->getInitializer() : 0;
-      GV->getParent()->getGlobalList().erase(GVar);
-      ++NumVariables;
-
-      // If there was an initializer for the global variable, try to destroy it
-      // now.
-      if (Init) DestroyInitializer(Init);
-
-      // If the global variable is still on the worklist, remove it now.
-      std::vector<GlobalValue*>::iterator I = std::find(WorkList.begin(),
-                                                        WorkList.end(), GV);
-      while (I != WorkList.end()) {
-        I = WorkList.erase(I);
-        I = std::find(I, WorkList.end(), GV);
-      }
-
-      return true;
-    }
-  } else {
-    Function *F = cast<Function>(GV);
-    // FIXME: TODO
-
+  for (Module::giterator I = M.gbegin(), E = M.gend(); I != E; ++I) {
+    Changed |= RemoveUnusedConstantPointerRef(*I);
+    // Externally visible globals are needed, if they have an initializer.
+    if (I->hasExternalLinkage() && !I->isExternal())
+      GlobalIsNeeded(I);
   }
-  return false;
-}
 
-// DestroyInitializer - A global variable was just destroyed and C is its
-// initializer. If we can, destroy C and all of the constants it refers to.
-//
-void GlobalDCE::DestroyInitializer(Constant *C) {
-  // Cannot destroy constants still being used, and cannot destroy primitive
-  // types.
-  if (!C->use_empty() || C->getType()->isPrimitiveType()) return;
 
-  // If this is a CPR, the global value referred to may be dead now!  Add it to
-  // the worklist.
+  // Now that all globals which are needed are in the AliveGlobals set, we loop
+  // through the program, deleting those which are not alive.
   //
-  if (ConstantPointerRef *CPR = dyn_cast<ConstantPointerRef>(C)) {
-    WorkList.push_back(CPR->getValue());
-    C->destroyConstant();
-    ++NumCPRs;
-  } else {
-    bool DestroyContents = true;
 
-    // As an optimization to the GlobalDCE algorithm, do attempt to destroy the
-    // contents of an array of primitive types, because we know that this will
-    // never succeed, and there could be a lot of them.
-    //
-    if (ConstantArray *CA = dyn_cast<ConstantArray>(C))
-      if (CA->getType()->getElementType()->isPrimitiveType())
-        DestroyContents = false;    // Nothing we can do with the subcontents
+  // The first pass is to drop initializers of global variables which are dead.
+  std::vector<GlobalVariable*> DeadGlobalVars;   // Keep track of dead globals
+  for (Module::giterator I = M.gbegin(), E = M.gend(); I != E; ++I)
+    if (!AliveGlobals.count(I)) {
+      DeadGlobalVars.push_back(I);         // Keep track of dead globals
+      I->setInitializer(0);
+    }
 
-    // All other constants refer to other constants.  Destroy them if possible
-    // as well.
-    //
-    std::vector<Value*> SubConstants;
-    if (DestroyContents) SubConstants.insert(SubConstants.end(),
-                                             C->op_begin(), C->op_end());
 
-    // Destroy the actual constant...
-    C->destroyConstant();
-    ++NumConsts;
+  // The second pass drops the bodies of functions which are dead...
+  std::vector<Function*> DeadFunctions;
+  for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I)
+    if (!AliveGlobals.count(I)) {
+      DeadFunctions.push_back(I);         // Keep track of dead globals
+      if (!I->isExternal())
+        I->deleteBody();
+    }
 
-    if (DestroyContents) {
-      // Remove duplicates from SubConstants, so that we do not call
-      // DestroyInitializer on the same constant twice (the first call might
-      // delete it, so this would be bad)
-      //
-      std::sort(SubConstants.begin(), SubConstants.end());
-      SubConstants.erase(std::unique(SubConstants.begin(), SubConstants.end()),
-                         SubConstants.end());
+  if (!DeadFunctions.empty()) {
+    // Now that all interreferences have been dropped, delete the actual objects
+    // themselves.
+    for (unsigned i = 0, e = DeadFunctions.size(); i != e; ++i) {
+      RemoveUnusedConstantPointerRef(*DeadFunctions[i]);
+      M.getFunctionList().erase(DeadFunctions[i]);
+    }
+    NumFunctions += DeadFunctions.size();
+    Changed = true;
+  }
 
-      // Loop over the subconstants, destroying them as well.
-      for (unsigned i = 0, e = SubConstants.size(); i != e; ++i)
-        DestroyInitializer(cast<Constant>(SubConstants[i]));
+  if (!DeadGlobalVars.empty()) {
+    for (unsigned i = 0, e = DeadGlobalVars.size(); i != e; ++i) {
+      RemoveUnusedConstantPointerRef(*DeadGlobalVars[i]);
+      M.getGlobalList().erase(DeadGlobalVars[i]);
     }
+    NumVariables += DeadGlobalVars.size();
+    Changed = true;
   }
+    
+  // Make sure that all memory is released
+  AliveGlobals.clear();
+  return Changed;
 }
 
-bool GlobalDCE::RemoveUnreachableGlobalVariables(Module &M) {
-  bool Changed = false;
-  WorkList.reserve(M.gsize());
+/// MarkGlobalIsNeeded - the specific global value as needed, and
+/// recursively mark anything that it uses as also needed.
+void GlobalDCE::GlobalIsNeeded(GlobalValue *G) {
+  std::set<GlobalValue*>::iterator I = AliveGlobals.lower_bound(G);
 
-  // Insert all of the globals into the WorkList, making sure to run
-  // RemoveUnusedConstantPointerRef at least once on all globals...
-  //
-  for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) {
-    Changed |= RemoveUnusedConstantPointerRef(*I);
-    WorkList.push_back(I);
-  }
-  for (Module::giterator I = M.gbegin(), E = M.gend(); I != E; ++I) {
-    Changed |= RemoveUnusedConstantPointerRef(*I);
-    WorkList.push_back(I);
-  }
+  // If the global is already in the set, no need to reprocess it.
+  if (I != AliveGlobals.end() && *I == G) return;
 
-  // Loop over the worklist, deleting global objects that we can.  Whenever we
-  // delete something that might make something else dead, it gets added to the
-  // worklist.
-  //
-  while (!WorkList.empty()) {
-    GlobalValue *GV = WorkList.back();
-    WorkList.pop_back();
+  // Otherwise insert it now, so we do not infinitely recurse
+  AliveGlobals.insert(I, G);
 
-    Changed |= RemoveIfDead(GV);
+  if (GlobalVariable *GV = dyn_cast<GlobalVariable>(G)) {
+    // If this is a global variable, we must make sure to add any global values
+    // referenced by the initializer to the alive set.
+    if (GV->hasInitializer())
+      MarkUsedGlobalsAsNeeded(GV->getInitializer());
+  } else {
+    // Otherwise this must be a function object.  We have to scan the body of
+    // the function looking for constants and global values which are used as
+    // operands.  Any operands of these types must be processed to ensure that
+    // any globals used will be marked as needed.
+    Function *F = cast<Function>(G);
+    // For all basic blocks...
+    for (Function::iterator BB = F->begin(), E = F->end(); BB != E; ++BB)
+      // For all instructions...
+      for (BasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ++I)
+        // For all operands...
+        for (User::op_iterator U = I->op_begin(), E = I->op_end(); U != E; ++U)
+          if (GlobalValue *GV = dyn_cast<GlobalValue>(*U))
+            GlobalIsNeeded(GV);
+          else if (Constant *C = dyn_cast<Constant>(*U))
+            MarkUsedGlobalsAsNeeded(C);      
   }
-
-  // Make sure that all memory is free'd from the worklist...
-  std::vector<GlobalValue*>().swap(WorkList);
-  return Changed;
 }
 
+void GlobalDCE::MarkUsedGlobalsAsNeeded(Constant *C) {
+  if (ConstantPointerRef *CPR = dyn_cast<ConstantPointerRef>(C))
+    GlobalIsNeeded(CPR->getValue());
+  else {
+    // Loop over all of the operands of the constant, adding any globals they
+    // use to the list of needed globals.
+    for (User::op_iterator I = C->op_begin(), E = C->op_end(); I != E; ++I)
+      MarkUsedGlobalsAsNeeded(cast<Constant>(*I));
+  }
+}
 
 // RemoveUnusedConstantPointerRef - Loop over all of the uses of the specified
 // GlobalValue, looking for the constant pointer ref that may be pointing to it.