From eedcd84803e8fcea24cde9e19c4f72da299f5164 Mon Sep 17 00:00:00 2001 From: Chris Lattner <sabre@nondot.org> Date: Mon, 31 Aug 2009 07:23:46 +0000 Subject: [PATCH] Step #1 to giving Callgraph some sane invariants. The problems with callgraph stem from the fact that we have two types of passes that need to update it: 1. callgraphscc and module passes that are explicitly aware of it 2. Functionpasses (and loop passes etc) that are interlaced with CGSCC passes by the CGSCC Passmgr. In the case of #1, we can reasonably expect the passes to update the call graph just like any analysis. However, functionpasses are not and generally should not be CG aware. This has caused us no end of problems, so this takes a new approach. Logically, the CGSCC Pass manager can rescan every function after it runs a function pass over it to see if the functionpass made any updates to the IR that affect the callgraph. This allows it to catch new calls introduced by the functionpass. In practice, doing this would be slow. This implementation keeps track of whether or not the current scc is dirtied by a function pass, and, if so, delays updating the callgraph until it is actually needed again. This was we avoid extraneous rescans, but we still have good invariants when the callgraph is needed. Step #2 of the "give Callgraph some sane invariants" is to change CallGraphNode to use a CallBackVH for the callsite entry of the CallGraphNode. This way we can immediately remove entries from the callgraph when a FunctionPass is active instead of having dangling pointers. The current pass tries to tolerate these dangling pointers, but it is just an evil hack. This is related to PR3601/4835/4029. This also reverts r80541, a hack working around the sad lack of invariants. llvm-svn: 80566 --- llvm/include/llvm/Analysis/CallGraph.h | 5 + llvm/lib/Analysis/IPA/CallGraph.cpp | 58 +++++---- llvm/lib/Analysis/IPA/CallGraphSCCPass.cpp | 139 ++++++++++++++++++++- 3 files changed, 175 insertions(+), 27 deletions(-) diff --git a/llvm/include/llvm/Analysis/CallGraph.h b/llvm/include/llvm/Analysis/CallGraph.h index bc022e3417e1..0e997cab7ff5 100644 --- a/llvm/include/llvm/Analysis/CallGraph.h +++ b/llvm/include/llvm/Analysis/CallGraph.h @@ -107,6 +107,7 @@ public: /// Returns the CallGraphNode which is used to represent undetermined calls /// into the callgraph. Override this if you want behavioral inheritance. virtual CallGraphNode* getExternalCallingNode() const { return 0; } + virtual CallGraphNode* getCallsExternalNode() const { return 0; } /// Return the root/main method in the module, or some other root node, such /// as the externalcallingnode. Overload these if you behavioral @@ -248,6 +249,10 @@ public: /// should be used sparingly. void removeCallEdgeFor(CallSite CS); + // FIXME: REMOVE THIS WHEN HACK IS REMOVED FROM CGSCCPASSMGR. + void removeCallEdgeFor(Instruction *CS); + + /// removeAnyCallEdgeTo - This method removes all call edges from this node /// to the specified callee function. This takes more time to execute than /// removeCallEdgeTo, so it should not be used unless necessary. diff --git a/llvm/lib/Analysis/IPA/CallGraph.cpp b/llvm/lib/Analysis/IPA/CallGraph.cpp index 6490642f7720..5757bfd897f6 100644 --- a/llvm/lib/Analysis/IPA/CallGraph.cpp +++ b/llvm/lib/Analysis/IPA/CallGraph.cpp @@ -239,22 +239,32 @@ void CallGraphNode::dump() const { print(errs()); } /// specified call site. Note that this method takes linear time, so it /// should be used sparingly. void CallGraphNode::removeCallEdgeFor(CallSite CS) { - // Scan for the call site. Note that we aren't guaranteed to find the call - // site in this node, even in reasonable situations. Passes like instcombine - // can adjust callsites (e.g. folding bitcasts into calls to make them direct) - // which can introduce new call sites. Since instcombine is not callgraph - // aware, it doesn't know to tell CallGraph about this change. - for (CalledFunctionsVector::iterator I = CalledFunctions.begin(); - I != CalledFunctions.end(); ++I) { - if (I->first != CS) continue; - - I->second->DropRef(); - *I = CalledFunctions.back(); - CalledFunctions.pop_back(); - return; + for (CalledFunctionsVector::iterator I = CalledFunctions.begin(); ; ++I) { + assert(I != CalledFunctions.end() && "Cannot find callsite to remove!"); + if (I->first == CS) { + I->second->DropRef(); + *I = CalledFunctions.back(); + CalledFunctions.pop_back(); + return; + } } } +// FIXME: REMOVE THIS WHEN HACK IS REMOVED FROM CGSCCPASSMGR. +void CallGraphNode::removeCallEdgeFor(Instruction *CS) { + for (CalledFunctionsVector::iterator I = CalledFunctions.begin(); ; ++I) { + assert(I != CalledFunctions.end() && "Cannot find callsite to remove!"); + if (I->first.getInstruction() == CS) { + I->second->DropRef(); + *I = CalledFunctions.back(); + CalledFunctions.pop_back(); + return; + } + } + +} + + // removeAnyCallEdgeTo - This method removes any call edges from this node to // the specified callee function. This takes more time to execute than @@ -291,18 +301,18 @@ void CallGraphNode::replaceCallSite(CallSite Old, CallSite New, CallGraphNode *NewCallee) { for (CalledFunctionsVector::iterator I = CalledFunctions.begin(); ; ++I) { assert(I != CalledFunctions.end() && "Cannot find callsite to replace!"); - if (I->first == Old) { - I->first = New; - - // If the callee is changing, not just the callsite, then update it as - // well. - if (NewCallee) { - I->second->DropRef(); - I->second = NewCallee; - I->second->AddRef(); - } - return; + if (I->first != Old) continue; + + I->first = New; + + // If the callee is changing, not just the callsite, then update it as + // well. + if (NewCallee) { + I->second->DropRef(); + I->second = NewCallee; + I->second->AddRef(); } + return; } } diff --git a/llvm/lib/Analysis/IPA/CallGraphSCCPass.cpp b/llvm/lib/Analysis/IPA/CallGraphSCCPass.cpp index d91a74dd4368..b4c03a976b23 100644 --- a/llvm/lib/Analysis/IPA/CallGraphSCCPass.cpp +++ b/llvm/lib/Analysis/IPA/CallGraphSCCPass.cpp @@ -15,11 +15,13 @@ // //===----------------------------------------------------------------------===// +#define DEBUG_TYPE "cgscc-passmgr" #include "llvm/CallGraphSCCPass.h" #include "llvm/Analysis/CallGraph.h" #include "llvm/ADT/SCCIterator.h" #include "llvm/PassManagers.h" #include "llvm/Function.h" +#include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; @@ -74,16 +76,24 @@ public: } private: - bool RunPassOnSCC(Pass *P, std::vector<CallGraphNode*> &CurSCC); + bool RunPassOnSCC(Pass *P, std::vector<CallGraphNode*> &CurSCC, + CallGraph &CG, bool &CallGraphUpToDate); + void RefreshCallGraph(std::vector<CallGraphNode*> &CurSCC, CallGraph &CG); }; } // end anonymous namespace. char CGPassManager::ID = 0; -bool CGPassManager::RunPassOnSCC(Pass *P, std::vector<CallGraphNode*> &CurSCC) { +bool CGPassManager::RunPassOnSCC(Pass *P, std::vector<CallGraphNode*> &CurSCC, + CallGraph &CG, bool &CallGraphUpToDate) { bool Changed = false; if (CallGraphSCCPass *CGSP = dynamic_cast<CallGraphSCCPass*>(P)) { + if (!CallGraphUpToDate) { + RefreshCallGraph(CurSCC, CG); + CallGraphUpToDate = true; + } + StartPassTimer(P); Changed = CGSP->runOnSCC(CurSCC); StopPassTimer(P); @@ -103,9 +113,118 @@ bool CGPassManager::RunPassOnSCC(Pass *P, std::vector<CallGraphNode*> &CurSCC) { } StopPassTimer(P); + // The the function pass(es) modified the IR, they may have clobbered the + // callgraph. + if (Changed && CallGraphUpToDate) { + DEBUG(errs() << "CGSCCPASSMGR: Pass Dirtied SCC: " + << P->getPassName() << '\n'); + CallGraphUpToDate = false; + } return Changed; } +void CGPassManager::RefreshCallGraph(std::vector<CallGraphNode*> &CurSCC, + CallGraph &CG) { + DenseMap<Instruction*, CallGraphNode*> CallSites; + + DEBUG(errs() << "CGSCCPASSMGR: Refreshing SCC with " << CurSCC.size() + << " nodes:\n"; + for (unsigned i = 0, e = CurSCC.size(); i != e; ++i) + CurSCC[i]->dump(); + ); + + bool MadeChange = false; + MadeChange = MadeChange; + + // Scan all functions in the SCC. + for (unsigned sccidx = 0, e = CurSCC.size(); sccidx != e; ++sccidx) { + CallGraphNode *CGN = CurSCC[sccidx]; + Function *F = CGN->getFunction(); + if (F == 0 || F->isDeclaration()) continue; + + // Walk the function body looking for call sites. Sync up the call sites in + // CGN with those actually in the function. + + // Get the set of call sites currently in the function. + for (CallGraphNode::iterator I = CGN->begin(), E = CGN->end(); I != E; ++I){ + assert(I->first.getInstruction() && + "Call site record in function should not be abstract"); + assert(!CallSites.count(I->first.getInstruction()) && + "Call site occurs in node multiple times"); + CallSites.insert(std::make_pair(I->first.getInstruction(), + I->second)); + } + + // Loop over all of the instructions in the function, getting the callsites. + for (Function::iterator BB = F->begin(), E = F->end(); BB != E; ++BB) + for (BasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ++I) { + CallSite CS = CallSite::get(I); + if (!CS.getInstruction()) continue; + + // If this call site already existed in the callgraph, just verify it + // matches up to expectations and remove it from CallSites. + DenseMap<Instruction*, CallGraphNode*>::iterator ExistingIt = + CallSites.find(CS.getInstruction()); + if (ExistingIt != CallSites.end()) { + CallGraphNode *ExistingNode = ExistingIt->second; + + // Remove from CallSites since we have now seen it. + CallSites.erase(ExistingIt); + + // Verify that the callee is right. + if (ExistingNode->getFunction() == CS.getCalledFunction()) + continue; + + // If not, we either went from a direct call to indirect, indirect to + // direct, or direct to different direct. + CallGraphNode *CalleeNode; + if (Function *Callee = CS.getCalledFunction()) + CalleeNode = CG.getOrInsertFunction(Callee); + else + CalleeNode = CG.getCallsExternalNode(); + + CGN->replaceCallSite(CS, CS, CalleeNode); + MadeChange = true; + continue; + } + + // If the call site didn't exist in the CGN yet, add it. We assume that + // newly introduced call sites won't be indirect. This could be fixed + // in the future. + CallGraphNode *CalleeNode; + if (Function *Callee = CS.getCalledFunction()) + CalleeNode = CG.getOrInsertFunction(Callee); + else + CalleeNode = CG.getCallsExternalNode(); + + CGN->addCalledFunction(CS, CalleeNode); + MadeChange = true; + } + + // After scanning this function, if we still have entries in callsites, then + // they are dangling pointers. Crap. Well, until we change CallGraph to + // use CallbackVH, we'll just zap them here. When we have that, this should + // turn into an assertion. + if (CallSites.empty()) continue; + + for (DenseMap<Instruction*, CallGraphNode*>::iterator I = CallSites.begin(), + E = CallSites.end(); I != E; ++I) + // FIXME: I had to add a special horrible form of removeCallEdgeFor to + // support this. Remove the Instruction* version of it when we can. + CGN->removeCallEdgeFor(I->first); + MadeChange = true; + CallSites.clear(); + } + + DEBUG(if (MadeChange) { + errs() << "CGSCCPASSMGR: Refreshed SCC is now:\n"; + for (unsigned i = 0, e = CurSCC.size(); i != e; ++i) + CurSCC[i]->dump(); + } else { + errs() << "CGSCCPASSMGR: SCC Refresh didn't change call graph.\n"; + } + ); +} /// run - Execute all of the passes scheduled for execution. Keep track of /// whether any of the passes modifies the module, and if so, return true. @@ -124,6 +243,15 @@ bool CGPassManager::runOnModule(Module &M) { ++CGI; + // CallGraphUpToDate - Keep track of whether the callgraph is known to be + // up-to-date or not. The CGSSC pass manager runs two types of passes: + // CallGraphSCC Passes and other random function passes. Because other + // random function passes are not CallGraph aware, they may clobber the + // call graph by introducing new calls or deleting other ones. This flag + // is set to false when we run a function pass so that we know to clean up + // the callgraph when we need to run a CGSCCPass again. + bool CallGraphUpToDate = true; + // Run all passes on current SCC. for (unsigned PassNo = 0, e = getNumContainedPasses(); PassNo != e; ++PassNo) { @@ -135,7 +263,7 @@ bool CGPassManager::runOnModule(Module &M) { initializeAnalysisImpl(P); // Actually run this pass on the current SCC. - Changed |= RunPassOnSCC(P, CurSCC); + Changed |= RunPassOnSCC(P, CurSCC, CG, CallGraphUpToDate); if (Changed) dumpPassInfo(P, MODIFICATION_MSG, ON_CG_MSG, ""); @@ -146,6 +274,11 @@ bool CGPassManager::runOnModule(Module &M) { recordAvailableAnalysis(P); removeDeadPasses(P, "", ON_CG_MSG); } + + // If the callgraph was left out of date (because the last pass run was a + // functionpass), refresh it before we move on to the next SCC. + if (!CallGraphUpToDate) + RefreshCallGraph(CurSCC, CG); } Changed |= doFinalization(CG); return Changed;