From 355e2ca1f4c31d99cfdb2b892a76fb9b10780d2c Mon Sep 17 00:00:00 2001 From: "Vikram S. Adve" Date: Tue, 30 Jul 2002 22:05:22 +0000 Subject: [PATCH] Use a separate globals graph to hold externally visible nodes. This changes both the bottom-up and top-down propagation so that globals and other external objects do not have to appear in every function, but only in functions in which they are referenced or they can be used to access something else that is referenced. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@3170 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../DataStructure/BottomUpClosure.cpp | 121 +++-- lib/Analysis/DataStructure/DataStructure.cpp | 470 +++++++++++++++--- 2 files changed, 484 insertions(+), 107 deletions(-) diff --git a/lib/Analysis/DataStructure/BottomUpClosure.cpp b/lib/Analysis/DataStructure/BottomUpClosure.cpp index c8b45b50ccd..3ea7f0c96ab 100644 --- a/lib/Analysis/DataStructure/BottomUpClosure.cpp +++ b/lib/Analysis/DataStructure/BottomUpClosure.cpp @@ -11,17 +11,18 @@ #include "llvm/Module.h" #include "llvm/DerivedTypes.h" #include "Support/StatisticReporter.h" +#include using std::map; static RegisterAnalysis -X("budatastructure", "Bottom-Up Data Structure Analysis Closure"); +X("budatastructure", "Bottom-up Data Structure Analysis Closure"); AnalysisID BUDataStructures::ID = X; // releaseMemory - If the pass pipeline is done with this pass, we can release // our memory... here... // void BUDataStructures::releaseMemory() { - for (map::iterator I = DSInfo.begin(), + for (map::iterator I = DSInfo.begin(), E = DSInfo.end(); I != E; ++I) delete I->second; @@ -60,23 +61,29 @@ static void ResolveArguments(std::vector &Call, Function &F, } } -// MergeGlobalNodes - Merge global value nodes in the inlined graph with the -// global value nodes in the current graph if there are duplicates. +// MergeGlobalNodes - Merge all existing global nodes with globals +// inlined from the callee or with globals from the GlobalsGraph. // -static void MergeGlobalNodes(map &ValMap, +static void MergeGlobalNodes(DSGraph& Graph, map &OldValMap) { - // Loop over all of the nodes inlined, if any of them are global variable - // nodes, we must make sure they get properly added or merged with the ValMap. - // + map &ValMap = Graph.getValueMap(); + for (map::iterator I = ValMap.begin(), E = ValMap.end(); + I != E; ++I) + if (GlobalValue* GV = dyn_cast(I->first)) { + map:: iterator NHI = OldValMap.find(GV); + if (NHI != OldValMap.end()) // was it inlined from the callee? + I->second->mergeWith(NHI->second); + else // get it from the GlobalsGraph + I->second->mergeWith(Graph.cloneGlobalInto(GV)); + } + + // Add unused inlined global nodes into the value map for (map::iterator I = OldValMap.begin(), E = OldValMap.end(); I != E; ++I) if (isa(I->first)) { - DSNodeHandle &NH = ValMap[I->first]; // Look up global in ValMap. - if (NH == 0) { // No entry for the global yet? - NH = I->second; // Add the one just inlined... - } else { - NH->mergeWith(I->second); // Merge the two globals together. - } + DSNodeHandle &NH = ValMap[I->first]; // If global is not in ValMap... + if (NH == 0) + NH = I->second; // Add the one just inlined. } } @@ -91,17 +98,29 @@ DSGraph &BUDataStructures::calculateGraph(Function &F) { // Copy the local version into DSInfo... Graph = new DSGraph(getAnalysis().getDSGraph(F)); + // Populate the GlobalsGraph with globals from this one. + Graph->GlobalsGraph->cloneGlobals(*Graph, /*cloneCalls*/ false); + // Save a copy of the original call nodes for the top-down pass Graph->saveOrigFunctionCalls(); - + // Start resolving calls... std::vector > &FCs = Graph->getFunctionCalls(); - DEBUG(std::cerr << "Inlining: " << F.getName() << "\n"); + DEBUG(std::cerr << " [BU] Inlining: " << F.getName() << "\n"); + + // Add F to the PendingCallers list of each direct callee for use in the + // top-down pass so we don't have to compute this again. We don't want + // to do it for indirect callees inlined later, so remember which calls + // are in the original FCs set. + std::set directCallees; + for (unsigned i = 0; i < FCs.size(); ++i) + directCallees.insert(FCs[i][1]); // ptr to function node bool Inlined; do { Inlined = false; + for (unsigned i = 0; i != FCs.size(); ++i) { // Copy the call, because inlining graphs may invalidate the FCs vector. std::vector Call = FCs[i]; @@ -111,17 +130,17 @@ DSGraph &BUDataStructures::calculateGraph(Function &F) { // Start inlining all of the functions we can... some may not be // inlinable if they are external... // - std::vector Globals(Call[1]->getGlobals()); + std::vector Callees(Call[1]->getGlobals()); // Loop over the functions, inlining whatever we can... - for (unsigned g = 0; g != Globals.size(); ++g) { + for (unsigned c = 0; c != Callees.size(); ++c) { // Must be a function type, so this cast MUST succeed. - Function &FI = cast(*Globals[g]); + Function &FI = cast(*Callees[c]); if (&FI == &F) { // Self recursion... simply link up the formal arguments with the // actual arguments... - - DEBUG(std::cerr << "Self Inlining: " << F.getName() << "\n"); + + DEBUG(std::cerr << "\t[BU] Self Inlining: " << F.getName() << "\n"); if (Call[0]) // Handle the return value if present... Graph->RetNode->mergeWith(Call[0]); @@ -129,10 +148,10 @@ DSGraph &BUDataStructures::calculateGraph(Function &F) { // Resolve the arguments in the call to the actual values... ResolveArguments(Call, F, Graph->getValueMap()); - // Erase the entry in the globals vector - Globals.erase(Globals.begin()+g--); + // Erase the entry in the callees vector + Callees.erase(Callees.begin()+c--); } else if (!FI.isExternal()) { - DEBUG(std::cerr << "In " << F.getName() << " inlining: " + DEBUG(std::cerr << "\t[BU] In " << F.getName() << " inlining: " << FI.getName() << "\n"); // Get the data structure graph for the called function, closing it @@ -141,49 +160,54 @@ DSGraph &BUDataStructures::calculateGraph(Function &F) { // DSGraph &GI = calculateGraph(FI); // Graph to inline - DEBUG(std::cerr << "Got graph for " << FI.getName() << " in: " - << F.getName() << "\n"); - - // Remember the callers for each callee for use in the top-down - // pass so we don't have to compute this again - GI.addCaller(F); + DEBUG(std::cerr << "\t\t[BU] Got graph for " << FI.getName() + << " in: " << F.getName() << "\n"); // Clone the callee's graph into the current graph, keeping - // track of where scalars in the old graph _used_ to point - // and of the new nodes matching nodes of the old graph ... + // track of where scalars in the old graph _used_ to point, + // and of the new nodes matching nodes of the old graph. std::map OldValMap; - std::map OldNodeMap; // unused + std::map OldNodeMap; // The clone call may invalidate any of the vectors in the data - // structure graph. - DSNode *RetVal = Graph->cloneInto(GI, OldValMap, OldNodeMap); + // structure graph. Strip locals and don't copy the list of callers + DSNode *RetVal = Graph->cloneInto(GI, OldValMap, OldNodeMap, + /*StripScalars*/ true, + /*StripAllocas*/ true, + /*CopyCallers*/ false, + /*CopyOrigCalls*/ false); ResolveArguments(Call, FI, OldValMap); if (Call[0]) // Handle the return value if present RetVal->mergeWith(Call[0]); - + // Merge global value nodes in the inlined graph with the global // value nodes in the current graph if there are duplicates. // - MergeGlobalNodes(Graph->getValueMap(), OldValMap); + MergeGlobalNodes(*Graph, OldValMap); + + // If this was an original call, add F to the PendingCallers list + if (directCallees.find(Call[1]) != directCallees.end()) + GI.addCaller(F); + + // Erase the entry in the Callees vector + Callees.erase(Callees.begin()+c--); - // Erase the entry in the globals vector - Globals.erase(Globals.begin()+g--); } else if (FI.getName() == "printf" || FI.getName() == "sscanf" || FI.getName() == "fprintf" || FI.getName() == "open" || FI.getName() == "sprintf") { // Erase the entry in the globals vector - Globals.erase(Globals.begin()+g--); + Callees.erase(Callees.begin()+c--); } } - if (Globals.empty()) { // Inlined all of the function calls? + if (Callees.empty()) { // Inlined all of the function calls? // Erase the call if it is resolvable... FCs.erase(FCs.begin()+i--); // Don't skip a the next call... Inlined = true; - } else if (Globals.size() != Call[1]->getGlobals().size()) { + } else if (Callees.size() != Call[1]->getGlobals().size()) { // Was able to inline SOME, but not all of the functions. Construct a // new global node here. // @@ -198,9 +222,20 @@ DSGraph &BUDataStructures::calculateGraph(Function &F) { if (Inlined) { Graph->maskIncompleteMarkers(); Graph->markIncompleteNodes(); - Graph->removeDeadNodes(); + Graph->removeDeadNodes(/*KeepAllGlobals*/ false, /*KeepCalls*/ true); } } while (Inlined && !FCs.empty()); + // Copy any unresolved call nodes into the Globals graph and + // filter out unresolved call nodes inlined from the callee. + if (!FCs.empty()) + Graph->GlobalsGraph->cloneCalls(*Graph); + + Graph->maskIncompleteMarkers(); + Graph->markIncompleteNodes(); + Graph->removeDeadNodes(/*KeepAllGlobals*/ false, /*KeepCalls*/ false); + + DEBUG(cerr << " [BU] Done inlining: " << F.getName() << "\n"); + return *Graph; } diff --git a/lib/Analysis/DataStructure/DataStructure.cpp b/lib/Analysis/DataStructure/DataStructure.cpp index 5d62f090730..7ef79211ac3 100644 --- a/lib/Analysis/DataStructure/DataStructure.cpp +++ b/lib/Analysis/DataStructure/DataStructure.cpp @@ -10,7 +10,6 @@ #include "Support/StatisticReporter.h" #include "Support/STLExtras.h" #include -#include #include "llvm/Analysis/DataStructure.h" using std::vector; @@ -126,12 +125,14 @@ void DSNode::mergeWith(DSNode *N) { // DSGraph Implementation //===----------------------------------------------------------------------===// -DSGraph::DSGraph(const DSGraph &G) : Func(G.Func) { +DSGraph::DSGraph(const DSGraph &G) : Func(G.Func), GlobalsGraph(G.GlobalsGraph){ + GlobalsGraph->addReference(this); std::map NodeMap; // ignored - RetNode = cloneInto(G, ValueMap, NodeMap, false); + RetNode = cloneInto(G, ValueMap, NodeMap); } DSGraph::~DSGraph() { + GlobalsGraph->removeReference(this); FunctionCalls.clear(); OrigFunctionCalls.clear(); ValueMap.clear(); @@ -151,6 +152,24 @@ DSGraph::~DSGraph() { void DSGraph::dump() const { print(std::cerr); } +// Helper function used to clone a function list. +// Each call really shd have an explicit representation as a separate class. +void +CopyFunctionCallsList(const std::vector >& fromCalls, + std::vector >& toCalls, + std::map& NodeMap) { + + unsigned FC = toCalls.size(); // FirstCall + toCalls.reserve(FC+fromCalls.size()); + for (unsigned i = 0, ei = fromCalls.size(); i != ei; ++i) { + toCalls.push_back(std::vector()); + toCalls[FC+i].reserve(fromCalls[i].size()); + for (unsigned j = 0, ej = fromCalls[i].size(); j != ej; ++j) + toCalls[FC+i].push_back(NodeMap[fromCalls[i][j]]); + } +} + + // cloneInto - Clone the specified DSGraph into the current graph, returning the // Return node of the graph. The translated ValueMap for the old function is // filled into the OldValMap member. If StripLocals is set to true, Scalar and @@ -160,13 +179,14 @@ void DSGraph::dump() const { print(std::cerr); } DSNode *DSGraph::cloneInto(const DSGraph &G, std::map &OldValMap, std::map &OldNodeMap, - bool StripLocals) { + bool StripScalars, bool StripAllocas, + bool CopyCallers, bool CopyOrigCalls) { - assert(OldNodeMap.size()==0 && "Return argument OldNodeMap should be empty"); + assert(OldNodeMap.size()==0 && "Return arg. OldNodeMap shd be empty"); - OldNodeMap[0] = 0; // Null pointer maps to null + OldNodeMap[0] = 0; // Null pointer maps to null - unsigned FN = Nodes.size(); // FirstNode... + unsigned FN = Nodes.size(); // First new node... // Duplicate all of the nodes, populating the node map... Nodes.reserve(FN+G.Nodes.size()); @@ -176,16 +196,18 @@ DSNode *DSGraph::cloneInto(const DSGraph &G, OldNodeMap[Old] = New; } - // Rewrite the links in the nodes to point into the current graph now. + // Rewrite the links in the new nodes to point into the current graph now. for (unsigned i = FN, e = Nodes.size(); i != e; ++i) for (unsigned j = 0, e = Nodes[i]->getNumLinks(); j != e; ++j) - Nodes[i]->setLink(j, OldNodeMap[Nodes[i]->getLink(j)]); + Nodes[i]->setLink(j, OldNodeMap.find(Nodes[i]->getLink(j))->second); - // If we are inlining this graph into the called function graph, remove local - // markers. - if (StripLocals) + // Remove local markers as specified + if (StripScalars || StripAllocas) { + char keepBits = ~((StripScalars? DSNode::ScalarNode : 0) | + (StripAllocas? DSNode::AllocaNode : 0)); for (unsigned i = FN, e = Nodes.size(); i != e; ++i) - Nodes[i]->NodeType &= ~(DSNode::AllocaNode | DSNode::ScalarNode); + Nodes[i]->NodeType &= keepBits; + } // Copy the value map... for (std::map::const_iterator I = G.ValueMap.begin(), @@ -193,24 +215,45 @@ DSNode *DSGraph::cloneInto(const DSGraph &G, OldValMap[I->first] = OldNodeMap[I->second]; // Copy the function calls list... - unsigned FC = FunctionCalls.size(); // FirstCall - FunctionCalls.reserve(FC+G.FunctionCalls.size()); - for (unsigned i = 0, e = G.FunctionCalls.size(); i != e; ++i) { - FunctionCalls.push_back(std::vector()); - FunctionCalls[FC+i].reserve(G.FunctionCalls[i].size()); - for (unsigned j = 0, e = G.FunctionCalls[i].size(); j != e; ++j) - FunctionCalls[FC+i].push_back(OldNodeMap[G.FunctionCalls[i][j]]); - } + CopyFunctionCallsList(G.FunctionCalls, FunctionCalls, OldNodeMap); + if (CopyOrigCalls) + CopyFunctionCallsList(G.OrigFunctionCalls, OrigFunctionCalls, OldNodeMap); // Copy the list of unresolved callers - PendingCallers.insert(PendingCallers.end(), - G.PendingCallers.begin(), G.PendingCallers.end()); + if (CopyCallers) + PendingCallers.insert(G.PendingCallers.begin(), G.PendingCallers.end()); // Return the returned node pointer... return OldNodeMap[G.RetNode]; } +// cloneGlobalInto - Clone the given global node and all its target links +// (and all their llinks, recursively). +// +DSNode* DSGraph::cloneGlobalInto(const DSNode* GNode) { + if (GNode == 0 || GNode->getGlobals().size() == 0) return 0; + + // If a clone has already been created for GNode, return it. + DSNodeHandle& ValMapEntry = ValueMap[GNode->getGlobals()[0]]; + if (ValMapEntry != 0) + return ValMapEntry; + + // Clone the node and update the ValMap. + DSNode* NewNode = new DSNode(*GNode); + ValMapEntry = NewNode; // j=0 case of loop below! + Nodes.push_back(NewNode); + for (unsigned j = 1, N = NewNode->getGlobals().size(); j < N; ++j) + ValueMap[NewNode->getGlobals()[j]] = NewNode; + + // Rewrite the links in the new node to point into the current graph. + for (unsigned j = 0, e = GNode->getNumLinks(); j != e; ++j) + NewNode->setLink(j, cloneGlobalInto(GNode->getLink(j))); + + return NewNode; +} + + // markIncompleteNodes - Mark the specified node as having contents that are not // known with the current analysis we have performed. Because a node makes all // of the nodes it can reach imcomplete if the node itself is incomplete, we @@ -240,11 +283,12 @@ static void markIncompleteNode(DSNode *N) { // scope of current analysis may have modified it), the 'Incomplete' flag is // added to the NodeType. // -void DSGraph::markIncompleteNodes() { +void DSGraph::markIncompleteNodes(bool markFormalArgs) { // Mark any incoming arguments as incomplete... - for (Function::aiterator I = Func.abegin(), E = Func.aend(); I != E; ++I) - if (isa(I->getType())) - markIncompleteNode(ValueMap[I]->getLink(0)); + if (markFormalArgs) + for (Function::aiterator I = Func.abegin(), E = Func.aend(); I != E; ++I) + if (isa(I->getType())) + markIncompleteNode(ValueMap[I]->getLink(0)); // Mark stuff passed into functions calls as being incomplete... for (unsigned i = 0, e = FunctionCalls.size(); i != e; ++i) { @@ -268,6 +312,18 @@ void DSGraph::markIncompleteNodes() { } } +// removeRefsToGlobal - Helper function that removes globals from the +// ValueMap so that the referrer count will go down to zero. +static void +removeRefsToGlobal(DSNode* N, std::map& ValueMap) { + while (!N->getGlobals().empty()) { + GlobalValue *GV = N->getGlobals().back(); + N->getGlobals().pop_back(); + ValueMap.erase(GV); + } +} + + // isNodeDead - This method checks to see if a node is dead, and if it isn't, it // checks to see if there are simple transformations that it can do to make it // dead. @@ -278,17 +334,14 @@ bool DSGraph::isNodeDead(DSNode *N) { return true; // Is it a function node or some other trivially unused global? - if ((N->NodeType & ~DSNode::GlobalNode) == 0 && + if (N->NodeType != 0 && + (N->NodeType & ~DSNode::GlobalNode) == 0 && N->getNumLinks() == 0 && N->getReferrers().size() == N->getGlobals().size()) { // Remove the globals from the valuemap, so that the referrer count will go // down to zero. - while (!N->getGlobals().empty()) { - GlobalValue *GV = N->getGlobals().back(); - N->getGlobals().pop_back(); - ValueMap.erase(GV); - } + removeRefsToGlobal(N, ValueMap); assert(N->getReferrers().empty() && "Referrers should all be gone now!"); return true; } @@ -296,28 +349,34 @@ bool DSGraph::isNodeDead(DSNode *N) { return false; } +static void +removeIdenticalCalls(std::vector >& Calls, + const string& where) { + // Remove trivially identical function calls + unsigned NumFns = Calls.size(); + std::sort(Calls.begin(), Calls.end()); + Calls.erase(std::unique(Calls.begin(), Calls.end()), + Calls.end()); + + DEBUG(if (NumFns != Calls.size()) + std::cerr << "Merged " << (NumFns-Calls.size()) + << " call nodes in " << where << "\n";); +} // removeTriviallyDeadNodes - After the graph has been constructed, this method // removes all unreachable nodes that are created because they got merged with // other nodes in the graph. These nodes will all be trivially unreachable, so // we don't have to perform any non-trivial analysis here. // -void DSGraph::removeTriviallyDeadNodes() { +void DSGraph::removeTriviallyDeadNodes(bool KeepAllGlobals) { for (unsigned i = 0; i != Nodes.size(); ++i) - if (isNodeDead(Nodes[i])) { // This node is dead! - delete Nodes[i]; // Free memory... - Nodes.erase(Nodes.begin()+i--); // Remove from node list... - } + if (! KeepAllGlobals || ! (Nodes[i]->NodeType & DSNode::GlobalNode)) + if (isNodeDead(Nodes[i])) { // This node is dead! + delete Nodes[i]; // Free memory... + Nodes.erase(Nodes.begin()+i--); // Remove from node list... + } - // Remove trivially identical function calls - unsigned NumFns = FunctionCalls.size(); - std::sort(FunctionCalls.begin(), FunctionCalls.end()); - FunctionCalls.erase(std::unique(FunctionCalls.begin(), FunctionCalls.end()), - FunctionCalls.end()); - - DEBUG(if (NumFns != FunctionCalls.size()) - std::cerr << "Merged " << (NumFns-FunctionCalls.size()) - << " call nodes in " << Func.getName() << "\n";); + removeIdenticalCalls(FunctionCalls, Func.getName()); } @@ -325,13 +384,126 @@ void DSGraph::removeTriviallyDeadNodes() { // stuff to be alive. // static void markAlive(DSNode *N, std::set &Alive) { - if (N == 0 || Alive.count(N)) return; + if (N == 0) return; Alive.insert(N); for (unsigned i = 0, e = N->getNumLinks(); i != e; ++i) - markAlive(N->getLink(i), Alive); + if (N->getLink(i) && !Alive.count(N->getLink(i))) + markAlive(N->getLink(i), Alive); } +static bool checkGlobalAlive(DSNode *N, std::set &Alive, + std::set &Visiting) { + if (N == 0) return false; + + if (Visiting.count(N) > 0) return false; // terminate recursion on a cycle + Visiting.insert(N); + + // If any immediate successor is alive, N is alive + for (unsigned i = 0, e = N->getNumLinks(); i != e; ++i) + if (N->getLink(i) && Alive.count(N->getLink(i))) + { Visiting.erase(N); return true; } + + // Else if any successor reaches a live node, N is alive + for (unsigned i = 0, e = N->getNumLinks(); i != e; ++i) + if (N->getLink(i) && checkGlobalAlive(N->getLink(i), Alive, Visiting)) + { Visiting.erase(N); return true; } + + Visiting.erase(N); + return false; +} + + +// markGlobalsIteration - Recursive helper function for markGlobalsAlive(). +// This would be unnecessary if function calls were real nodes! In that case, +// the simple iterative loop in the first few lines below suffice. +// +static void markGlobalsIteration(std::set& GlobalNodes, + std::vector > &Calls, + std::set &Alive, + bool FilterCalls) { + + // Iterate, marking globals or cast nodes alive until no new live nodes + // are added to Alive + std::set Visiting; // Used to identify cycles + std::set::iterator I=GlobalNodes.begin(), E=GlobalNodes.end(); + for (size_t liveCount = 0; liveCount < Alive.size(); ) { + liveCount = Alive.size(); + for ( ; I != E; ++I) + if (Alive.count(*I) == 0) { + Visiting.clear(); + if (checkGlobalAlive(*I, Alive, Visiting)) + markAlive(*I, Alive); + } + } + + // Find function calls with some dead and some live nodes. + // Since all call nodes must be live if any one is live, we have to mark + // all nodes of the call as live and continue the iteration (via recursion). + if (FilterCalls) { + bool recurse = false; + for (int i = 0, ei = Calls.size(); i < ei; ++i) { + bool CallIsDead = true, CallHasDeadArg = false; + for (unsigned j = 0, ej = Calls[i].size(); j != ej; ++j) { + bool argIsDead = Calls[i][j] == 0 || Alive.count(Calls[i][j]) == 0; + CallHasDeadArg = CallHasDeadArg || (Calls[i][j] != 0 && argIsDead); + CallIsDead = CallIsDead && argIsDead; + } + if (!CallIsDead && CallHasDeadArg) { + // Some node in this call is live and another is dead. + // Mark all nodes of call as live and iterate once more. + recurse = true; + for (unsigned j = 0, ej = Calls[i].size(); j != ej; ++j) + markAlive(Calls[i][j], Alive); + } + } + if (recurse) + markGlobalsIteration(GlobalNodes, Calls, Alive, FilterCalls); + } +} + + +// markGlobalsAlive - Mark global nodes and cast nodes alive if they +// can reach any other live node. Since this can produce new live nodes, +// we use a simple iterative algorithm. +// +static void markGlobalsAlive(DSGraph& G, std::set &Alive, + bool FilterCalls) { + // Add global and cast nodes to a set so we don't walk all nodes every time + std::set GlobalNodes; + for (unsigned i = 0, e = G.getNodes().size(); i != e; ++i) + if (G.getNodes()[i]->NodeType & (DSNode::CastNode | DSNode::GlobalNode)) + GlobalNodes.insert(G.getNodes()[i]); + + // Add all call nodes to the same set + std::vector > &Calls = G.getFunctionCalls(); + if (FilterCalls) { + for (unsigned i = 0, e = Calls.size(); i != e; ++i) + for (unsigned j = 0, e = Calls[i].size(); j != e; ++j) + if (Calls[i][j]) + GlobalNodes.insert(Calls[i][j]); + } + + // Iterate and recurse until no new live node are discovered. + // This would be a simple iterative loop if function calls were real nodes! + markGlobalsIteration(GlobalNodes, Calls, Alive, FilterCalls); + + // Free up references to dead globals from the ValueMap + std::set::iterator I=GlobalNodes.begin(), E=GlobalNodes.end(); + for( ; I != E; ++I) + if (Alive.count(*I) == 0) + removeRefsToGlobal(*I, G.getValueMap()); + + // Delete dead function calls + if (FilterCalls) + for (int ei = Calls.size(), i = ei-1; i >= 0; --i) { + bool CallIsDead = true; + for (unsigned j = 0, ej= Calls[i].size(); CallIsDead && j != ej; ++j) + CallIsDead = (Alive.count(Calls[i][j]) == 0); + if (CallIsDead) + Calls.erase(Calls.begin() + i); // remove the call entirely + } +} // removeDeadNodes - Use a more powerful reachability analysis to eliminate // subgraphs that are unreachable. This often occurs because the data @@ -339,30 +511,44 @@ static void markAlive(DSNode *N, std::set &Alive) { // from the caller's graph entirely. This is only appropriate to use when // inlining graphs. // -void DSGraph::removeDeadNodes() { +void DSGraph::removeDeadNodes(bool KeepAllGlobals, bool KeepCalls) { + assert((!KeepAllGlobals || KeepCalls) && + "KeepAllGlobals without KeepCalls is meaningless"); + // Reduce the amount of work we have to do... - removeTriviallyDeadNodes(); - + removeTriviallyDeadNodes(KeepAllGlobals); + // FIXME: Merge nontrivially identical call nodes... // Alive - a set that holds all nodes found to be reachable/alive. std::set Alive; - // Mark all nodes reachable by call nodes as alive... - for (unsigned i = 0, e = FunctionCalls.size(); i != e; ++i) - for (unsigned j = 0, e = FunctionCalls[i].size(); j != e; ++j) - markAlive(FunctionCalls[i][j], Alive); + // If KeepCalls, mark all nodes reachable by call nodes as alive... + if (KeepCalls) + for (unsigned i = 0, e = FunctionCalls.size(); i != e; ++i) + for (unsigned j = 0, e = FunctionCalls[i].size(); j != e; ++j) + markAlive(FunctionCalls[i][j], Alive); for (unsigned i = 0, e = OrigFunctionCalls.size(); i != e; ++i) for (unsigned j = 0, e = OrigFunctionCalls[i].size(); j != e; ++j) markAlive(OrigFunctionCalls[i][j], Alive); - // Mark all nodes reachable by scalar, global, or incomplete nodes as - // reachable... + // Mark all nodes reachable by scalar nodes (and global nodes, if + // keeping them was specified) as alive... + char keepBits = DSNode::ScalarNode | (KeepAllGlobals? DSNode::GlobalNode : 0); for (unsigned i = 0, e = Nodes.size(); i != e; ++i) - if (Nodes[i]->NodeType & (DSNode::ScalarNode | DSNode::GlobalNode)) + if (Nodes[i]->NodeType & keepBits) markAlive(Nodes[i], Alive); + // The return value is alive as well... + markAlive(RetNode, Alive); + + // Mark all globals or cast nodes that can reach a live node as alive. + // This also marks all nodes reachable from such nodes as alive. + // Of course, if KeepAllGlobals is specified, they would be live already. + if (! KeepAllGlobals) + markGlobalsAlive(*this, Alive, ! KeepCalls); + // Loop over all unreachable nodes, dropping their references... std::vector DeadNodes; DeadNodes.reserve(Nodes.size()); // Only one allocation is allowed. @@ -374,9 +560,6 @@ void DSGraph::removeDeadNodes() { N->dropAllReferences(); // Drop all outgoing edges } - // The return value is alive as well... - markAlive(RetNode, Alive); - // Delete all dead nodes... std::for_each(DeadNodes.begin(), DeadNodes.end(), deleter); } @@ -392,6 +575,162 @@ void DSGraph::maskNodeTypes(unsigned char Mask) { } +//===----------------------------------------------------------------------===// +// GlobalDSGraph Implementation +//===----------------------------------------------------------------------===// + +GlobalDSGraph::GlobalDSGraph() : DSGraph(*(Function*)0, this) { +} + +GlobalDSGraph::~GlobalDSGraph() { + assert(Referrers.size() == 0 && + "Deleting global graph while references from other graphs exist"); +} + +void GlobalDSGraph::addReference(const DSGraph* referrer) { + if (referrer != this) + Referrers.insert(referrer); +} + +void GlobalDSGraph::removeReference(const DSGraph* referrer) { + if (referrer != this) { + assert(Referrers.find(referrer) != Referrers.end() && "This is very bad!"); + Referrers.erase(referrer); + if (Referrers.size() == 0) + delete this; + } +} + +// Bits used in the next function +static const char ExternalTypeBits = (DSNode::GlobalNode | DSNode::NewNode | + DSNode::SubElement | DSNode::CastNode); + + +// GlobalDSGraph::cloneNodeInto - Clone a global node and all its externally +// visible target links (and recursively their such links) into this graph. +// NodeCache maps the node being cloned to its clone in the Globals graph, +// in order to track cycles. +// GlobalsAreFinal is a flag that says whether it is safe to assume that +// an existing global node is complete. This is important to avoid +// reinserting all globals when inserting Calls to functions. +// This is a helper function for cloneGlobals and cloneCalls. +// +DSNode* GlobalDSGraph::cloneNodeInto(DSNode *OldNode, + std::map &NodeCache, + bool GlobalsAreFinal) { + if (OldNode == 0) return 0; + + // The caller should check this is an external node. Just more efficient... + assert((OldNode->NodeType & ExternalTypeBits) && "Non-external node"); + + // If a clone has already been created for OldNode, return it. + DSNode*& CacheEntry = NodeCache[OldNode]; + if (CacheEntry != 0) + return CacheEntry; + + // The result value... + DSNode* NewNode = 0; + + // If nodes already exist for any of the globals of OldNode, + // merge all such nodes together since they are merged in OldNode. + // If ValueCacheIsFinal==true, look for an existing node that has + // an identical list of globals and return it if it exists. + // + for (unsigned j = 0, N = OldNode->getGlobals().size(); j < N; ++j) + if (DSNode* PrevNode = ValueMap[OldNode->getGlobals()[j]]) { + if (NewNode == 0) { + NewNode = PrevNode; // first existing node found + if (GlobalsAreFinal && j == 0) + if (OldNode->getGlobals() == PrevNode->getGlobals()) { + CacheEntry = NewNode; + return NewNode; + } + } + else if (NewNode != PrevNode) { // found another, different from prev + // update ValMap *before* merging PrevNode into NewNode + for (unsigned k = 0, NK = PrevNode->getGlobals().size(); k < NK; ++k) + ValueMap[PrevNode->getGlobals()[k]] = NewNode; + NewNode->mergeWith(PrevNode); + } + } else if (NewNode != 0) { + ValueMap[OldNode->getGlobals()[j]] = NewNode; // add the merged node + } + + // If no existing node was found, clone the node and update the ValMap. + if (NewNode == 0) { + NewNode = new DSNode(*OldNode); + Nodes.push_back(NewNode); + for (unsigned j = 0, e = NewNode->getNumLinks(); j != e; ++j) + NewNode->setLink(j, 0); + for (unsigned j = 0, N = NewNode->getGlobals().size(); j < N; ++j) + ValueMap[NewNode->getGlobals()[j]] = NewNode; + } + else + NewNode->NodeType |= OldNode->NodeType; // Markers may be different! + + // Add the entry to NodeCache + CacheEntry = NewNode; + + // Rewrite the links in the new node to point into the current graph, + // but only for links to external nodes. Set other links to NULL. + for (unsigned j = 0, e = OldNode->getNumLinks(); j != e; ++j) { + DSNode* OldTarget = OldNode->getLink(j); + if (OldTarget && (OldTarget->NodeType & ExternalTypeBits)) { + DSNode* NewLink = this->cloneNodeInto(OldTarget, NodeCache); + if (NewNode->getLink(j)) + NewNode->getLink(j)->mergeWith(NewLink); + else + NewNode->setLink(j, NewLink); + } + } + + // Remove all local markers + NewNode->NodeType &= ~(DSNode::AllocaNode | DSNode::ScalarNode); + + return NewNode; +} + + +// GlobalDSGraph::cloneGlobals - Clone global nodes and all their externally +// visible target links (and recursively their such links) into this graph. +// +void GlobalDSGraph::cloneGlobals(DSGraph& Graph, bool CloneCalls) { + std::map NodeCache; + for (unsigned i = 0, N = Graph.Nodes.size(); i < N; ++i) + if (Graph.Nodes[i]->NodeType & DSNode::GlobalNode) + GlobalsGraph->cloneNodeInto(Graph.Nodes[i], NodeCache, false); + + if (CloneCalls) + GlobalsGraph->cloneCalls(Graph); + + GlobalsGraph->removeDeadNodes(/*KeepAllGlobals*/ true, /*KeepCalls*/ true); +} + + +// GlobalDSGraph::cloneCalls - Clone function calls and their visible target +// links (and recursively their such links) into this graph. +// +void GlobalDSGraph::cloneCalls(DSGraph& Graph) { + std::map NodeCache; + std::vector >& FromCalls =Graph.FunctionCalls; + + FunctionCalls.reserve(FunctionCalls.size() + FromCalls.size()); + + for (int i = 0, ei = FromCalls.size(); i < ei; ++i) { + FunctionCalls.push_back(std::vector()); + FunctionCalls.back().reserve(FromCalls[i].size()); + for (unsigned j = 0, ej = FromCalls[i].size(); j != ej; ++j) + FunctionCalls.back().push_back + ((FromCalls[i][j] && (FromCalls[i][j]->NodeType & ExternalTypeBits)) + ? cloneNodeInto(FromCalls[i][j], NodeCache, true) + : 0); + } + + // remove trivially identical function calls + removeIdenticalCalls(FunctionCalls, string("Globals Graph")); +} + + //===----------------------------------------------------------------------===// // LocalDataStructures Implementation //===----------------------------------------------------------------------===// @@ -400,7 +739,7 @@ void DSGraph::maskNodeTypes(unsigned char Mask) { // our memory... here... // void LocalDataStructures::releaseMemory() { - for (std::map::iterator I = DSInfo.begin(), + for (std::map::iterator I = DSInfo.begin(), E = DSInfo.end(); I != E; ++I) delete I->second; @@ -410,10 +749,13 @@ void LocalDataStructures::releaseMemory() { } bool LocalDataStructures::run(Module &M) { + // Create a globals graph for the module. Deleted when all graphs go away. + GlobalDSGraph* GG = new GlobalDSGraph; + // Calculate all of the graphs... for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) if (!I->isExternal()) - DSInfo.insert(std::make_pair(&*I, new DSGraph(*I))); + DSInfo.insert(std::make_pair(&*I, new DSGraph(*I, GG))); return false; }