//===- BottomUpClosure.cpp - Compute bottom-up interprocedural closure ----===// // // This file implements the BUDataStructures class, which represents the // Bottom-Up Interprocedural closure of the data structure graph over the // program. This is useful for applications like pool allocation, but **not** // applications like alias analysis. // //===----------------------------------------------------------------------===// #include "llvm/Analysis/DataStructure.h" #include "llvm/Analysis/DSGraph.h" #include "llvm/Module.h" //#include "llvm/DerivedTypes.h" #include "Support/Statistic.h" //#include using std::map; static RegisterAnalysis X("budatastructure", "Bottom-up Data Structure Analysis Closure"); // TODO: FIXME namespace DataStructureAnalysis { // isPointerType - Return true if this first class type is big enough to hold // a pointer. // bool isPointerType(const Type *Ty); } using namespace DataStructureAnalysis; // 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(), 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 bottom up data structure graphs for each function in the // program. // bool BUDataStructures::run(Module &M) { // Simply calculate the graphs for each function... for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) if (!I->isExternal()) calculateGraph(*I); return false; } // ResolveArguments - Resolve the formal and actual arguments for a function // call. // static void ResolveArguments(std::vector &Call, Function &F, map &ValueMap) { // Resolve all of the function arguments... Function::aiterator AI = F.abegin(); for (unsigned i = 2, e = Call.size(); i != e; ++i) { // Advance the argument iterator to the first pointer argument... while (!isPointerType(AI->getType())) ++AI; // Add the link from the argument scalar to the provided value DSNodeHandle &NN = ValueMap[AI]; NN.addEdgeTo(Call[i]); ++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); #if 0 else // get it from the GlobalsGraph I->second.mergeWith(Graph.cloneGlobalInto(GV)); #endif } // 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.getNode() == 0) NH = I->second; // Add the one just inlined. } } DSGraph &BUDataStructures::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... Graph = new DSGraph(getAnalysis().getDSGraph(F)); #if 0 // 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(); #endif // Start resolving calls... std::vector > &FCs = Graph->getFunctionCalls(); DEBUG(std::cerr << " [BU] Inlining: " << F.getName() << "\n"); #if 0 // 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 #endif 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]; // If the function list is complete... if ((Call[1].getNode()->NodeType & DSNode::Incomplete) == 0) { // Start inlining all of the functions we can... some may not be // inlinable if they are external... // std::vector Callees(Call[1].getNode()->getGlobals()); // Loop over the functions, inlining whatever we can... for (unsigned c = 0; c != Callees.size(); ++c) { // Must be a function type, so this cast MUST succeed. Function &FI = cast(*Callees[c]); if (&FI == &F) { // Self recursion... simply link up the formal arguments with the // actual arguments... DEBUG(std::cerr << "\t[BU] Self Inlining: " << F.getName() << "\n"); if (Call[0].getNode()) // Handle the return value if present... Graph->getRetNode().mergeWith(Call[0]); // Resolve the arguments in the call to the actual values... ResolveArguments(Call, F, Graph->getValueMap()); // Erase the entry in the callees vector Callees.erase(Callees.begin()+c--); } else if (!FI.isExternal()) { DEBUG(std::cerr << "\t[BU] In " << F.getName() << " inlining: " << FI.getName() << "\n"); // Get the data structure graph for the called function, closing it // if possible (which is only impossible in the case of mutual // recursion... // DSGraph &GI = calculateGraph(FI); // Graph to inline 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. map OldValMap; map OldNodeMap; // The clone call may invalidate any of the vectors in the data // structure graph. Strip locals and don't copy the list of callers DSNodeHandle RetVal = Graph->cloneInto(GI, OldValMap, OldNodeMap, /*StripScalars*/ true, /*StripAllocas*/ true, /*CopyCallers*/ false, /*CopyOrigCalls*/ false); // Resolve the arguments in the call to the actual values... ResolveArguments(Call, FI, OldValMap); if (Call[0].getNode()) // 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, OldValMap); #if 0 // If this was an original call, add F to the PendingCallers list if (directCallees.find(Call[1]) != directCallees.end()) GI.addCaller(F); #endif // Erase the entry in the Callees vector Callees.erase(Callees.begin()+c--); } else if (FI.getName() == "printf" || FI.getName() == "sscanf" || FI.getName() == "fprintf" || FI.getName() == "open" || FI.getName() == "sprintf") { // Erase the entry in the globals vector Callees.erase(Callees.begin()+c--); } } 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 (Callees.size() != Call[1].getNode()->getGlobals().size()) { // Was able to inline SOME, but not all of the functions. Construct a // new global node here. // assert(0 && "Unimpl!"); Inlined = true; } } } // Recompute the Incomplete markers. If there are any function calls left // now that are complete, we must loop! if (Inlined) { Graph->maskIncompleteMarkers(); Graph->markIncompleteNodes(); Graph->removeDeadNodes(/*KeepAllGlobals*/ true, /*KeepCalls*/ true); } } while (Inlined && !FCs.empty()); #if 0 // 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); #endif Graph->maskIncompleteMarkers(); Graph->markIncompleteNodes(); Graph->removeTriviallyDeadNodes(false); Graph->removeDeadNodes(/*KeepAllGlobals*/ true, /*KeepCalls*/ true); DEBUG(std::cerr << " [BU] Done inlining: " << F.getName() << " [" << Graph->getGraphSize() << "+" << Graph->getFunctionCalls().size() << "]\n"); return *Graph; }