diff --git a/lib/Analysis/DataStructure/TopDownClosure.cpp b/lib/Analysis/DataStructure/TopDownClosure.cpp new file mode 100644 index 00000000000..8f8e2ecf51c --- /dev/null +++ b/lib/Analysis/DataStructure/TopDownClosure.cpp @@ -0,0 +1,224 @@ +//===- TopDownClosure.cpp - Compute the top-down interprocedure closure ---===// +// +// This file implements the TDDataStructures class, which represents the +// Top-down Interprocedural closure of the data structure graph over the +// program. This is useful (but not strictly necessary?) for applications +// like pointer analysis. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Analysis/DataStructure.h" +#include "llvm/Module.h" +#include "llvm/DerivedTypes.h" +#include "Support/StatisticReporter.h" +using std::map; + +static RegisterAnalysis +Y("tddatastructure", "Top-down Data Structure Analysis Closure"); +AnalysisID TDDataStructures::ID = Y; + +// releaseMemory - If the pass pipeline is done with this pass, we can release +// our memory... here... +// +void TDDataStructures::releaseMemory() { + for (map::iterator I = DSInfo.begin(), + E = DSInfo.end(); I != E; ++I) + delete I->second; + + // Empty map so next time memory is released, data structures are not + // re-deleted. + DSInfo.clear(); +} + +// run - Calculate the top down data structure graphs for each function in the +// program. +// +bool TDDataStructures::run(Module &M) { + // Simply calculate the graphs for each function... + for (Module::reverse_iterator I = M.rbegin(), E = M.rend(); I != E; ++I) + if (!I->isExternal()) + calculateGraph(*I); + return false; +} + + +// ResolveArguments - Resolve the formal and actual arguments for a function +// call by merging the nodes for the actual arguments at the call site Call[] +// (these were copied over from the caller's graph into the callee's graph +// by cloneInto, and the nodes can be found from OldNodeMap) with the +// corresponding nodes for the formal arguments in the callee. +// +static void ResolveArguments(std::vector &Call, + Function &callee, + std::map &CalleeValueMap, + std::map OldNodeMap, + bool ignoreNodeMap) { + // Resolve all of the function formal arguments... + Function::aiterator AI = callee.abegin(); + for (unsigned i = 2, e = Call.size(); i != e; ++i) { + // Advance the argument iterator to the first pointer argument... + while (!isa(AI->getType())) ++AI; + + // TD ...Merge the formal arg scalar with the actual arg node + DSNode* actualArg = Call[i]; + DSNode *nodeForActual = ignoreNodeMap? actualArg : OldNodeMap[actualArg]; + assert(nodeForActual && "No node found for actual arg in callee graph!"); + + DSNode *nodeForFormal = CalleeValueMap[AI]->getLink(0); + if (nodeForFormal) + nodeForFormal->mergeWith(nodeForActual); + ++AI; + } +} + +// MergeGlobalNodes - Merge all existing global nodes with globals +// inlined from the callee or with globals from the GlobalsGraph. +// +static void MergeGlobalNodes(DSGraph& Graph, + map &OldValMap) { + 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]; // If global is not in ValMap... + if (NH == 0) + NH = I->second; // Add the one just inlined. + } +} + +// Helper function to push a caller's graph into the calleeGraph, +// once per call between the caller and the callee. +// Remove each such resolved call from the OrigFunctionCalls vector. +// NOTE: This may produce O(N^2) behavior because it uses linear search +// through the vector OrigFunctionCalls to find all calls to this callee. +// +void TDDataStructures::pushGraphIntoCallee(DSGraph &callerGraph, + DSGraph &calleeGraph, + std::map &OldValMap, + std::map &OldNodeMap) { + + Function& caller = callerGraph.getFunction(); + + // Loop over all function calls in the caller to find those to this callee + std::vector >& FunctionCalls = + callerGraph.getOrigFunctionCalls(); + + for (unsigned i = 0, ei = FunctionCalls.size(); i != ei; ++i) { + + std::vector& Call = FunctionCalls[i]; + assert(Call.size() >= 2 && "No function pointer in Call?"); + DSNodeHandle& callee = Call[1]; + std::vector funcPtrs(callee->getGlobals()); + + // Loop over the function pointers in the call, looking for the callee + for (unsigned f = 0; f != funcPtrs.size(); ++f) { + + // Must be a function type, so this cast MUST succeed. + Function &callee = cast(*funcPtrs[f]); + if (&callee != &calleeGraph.getFunction()) + continue; + + // Found a call to the callee. Inline its graph + // copy caller pointer because inlining may modify the callers vector + + // Merge actual arguments from the caller with formals in the callee. + // Don't use the old->new node map if this is a self-recursive call. + ResolveArguments(Call, callee, calleeGraph.getValueMap(), OldNodeMap, + /*ignoreNodeMap*/ &caller == &callee); + + // If its not a self-recursive call, merge global nodes in the inlined + // graph with the corresponding global nodes in the current graph + if (&caller != &callee) + MergeGlobalNodes(calleeGraph, OldValMap); + + // Merge returned node in the caller with the "return" node in callee + if (Call[0]) + calleeGraph.getRetNode()->mergeWith(OldNodeMap[Call[0]]); + + // Erase the entry in the globals vector + funcPtrs.erase(funcPtrs.begin()+f--); + + } // for each function pointer in the call node + } // for each original call node +} + + +DSGraph &TDDataStructures::calculateGraph(Function &F) { + // Make sure this graph has not already been calculated, or that we don't get + // into an infinite loop with mutually recursive functions. + // + DSGraph *&Graph = DSInfo[&F]; + if (Graph) return *Graph; + + // Copy the local version into DSInfo... + DSGraph& BUGraph = getAnalysis().getDSGraph(F); + Graph = new DSGraph(BUGraph); + + // Find the callers of this function recorded during the BU pass + std::set &PendingCallers = BUGraph.getPendingCallers(); + + DEBUG(cerr << " [TD] Inlining callers for: " << F.getName() << "\n"); + + for (std::set::iterator I=PendingCallers.begin(), + E=PendingCallers.end(); I != E; ++I) { + Function& caller = **I; + assert(! caller.isExternal() && "Externals unexpected in callers list"); + + DEBUG(std::cerr << "\t [TD] Inlining " << caller.getName() + << " into callee: " << F.getName() << "\n"); + + // These two maps keep track of where scalars in the old graph _used_ + // to point to, and of new nodes matching nodes of the old graph. + // These remain empty if no other graph is inlined (i.e., self-recursive). + std::map OldNodeMap; + std::map OldValMap; + + if (&caller == &F) { + // Self-recursive call: this can happen after a cycle of calls is inlined. + pushGraphIntoCallee(*Graph, *Graph, OldValMap, OldNodeMap); + } + else { + // Recursively compute the graph for the caller. That should + // be fully resolved except if there is mutual recursion... + // + DSGraph &callerGraph = calculateGraph(caller); // Graph to inline + + DEBUG(cerr << "\t\t[TD] Got graph for " << caller.getName() << " in: " + << F.getName() << "\n"); + + // Clone the caller's graph into the current graph, keeping + // track of where scalars in the old graph _used_ to point... + // Do this here because it only needs to happens once for each caller! + // Strip scalars but not allocas since they are visible in callee. + // + DSNode *RetVal = Graph->cloneInto(callerGraph, OldValMap, OldNodeMap, + /*StripScalars*/ true, + /*StripAllocas*/ false, + /*CopyCallers*/ true, + /*CopyOrigCalls*/ false); + + pushGraphIntoCallee(callerGraph, *Graph, OldValMap, OldNodeMap); + } + } + + // Recompute the Incomplete markers and eliminate unreachable nodes. + Graph->maskIncompleteMarkers(); + Graph->markIncompleteNodes(/*markFormals*/ ! F.hasInternalLinkage() + /*&& FIXME: NEED TO CHECK IF ALL CALLERS FOUND!*/); + Graph->removeDeadNodes(/*KeepAllGlobals*/ false, /*KeepCalls*/ false); + + DEBUG(cerr << " [TD] Done inlining callers for: " << F.getName() << "\n"); + + return *Graph; +}