/// InlineCallersIntoGraph - Inline all of the callers of the specified DS graph /// into it, then recompute completeness of nodes in the resultant graph. void TDDataStructures::InlineCallersIntoGraph(DSGraph* DSG) { // Inline caller graphs into this graph. First step, get the list of call // sites that call into this graph. std::vector<CallerCallEdge> EdgesFromCaller; std::map<DSGraph*, std::vector<CallerCallEdge> >::iterator CEI = CallerEdges.find(DSG); if (CEI != CallerEdges.end()) { std::swap(CEI->second, EdgesFromCaller); CallerEdges.erase(CEI); } // Sort the caller sites to provide a by-caller-graph ordering. std::sort(EdgesFromCaller.begin(), EdgesFromCaller.end()); // Merge information from the globals graph into this graph. FIXME: This is // stupid. Instead of us cloning information from the GG into this graph, // then having RemoveDeadNodes clone it back, we should do all of this as a // post-pass over all of the graphs. We need to take cloning out of // removeDeadNodes and gut removeDeadNodes at the same time first though. :( cloneGlobalsInto(DSG, DSGraph::DontCloneCallNodes | DSGraph::DontCloneAuxCallNodes); DEBUG(errs() << "[TD] Inlining callers into '" << DSG->getFunctionNames() << "'\n"); DSG->maskIncompleteMarkers(); // Iteratively inline caller graphs into this graph. while (!EdgesFromCaller.empty()) { DSGraph* CallerGraph = EdgesFromCaller.back().CallerGraph; // Iterate through all of the call sites of this graph, cloning and merging // any nodes required by the call. ReachabilityCloner RC(DSG, CallerGraph, DSGraph::DontCloneCallNodes | DSGraph::DontCloneAuxCallNodes); // Inline all call sites from this caller graph. do { const DSCallSite &CS = *EdgesFromCaller.back().CS; const Function &CF = *EdgesFromCaller.back().CalledFunction; DEBUG(errs() << " [TD] Inlining graph into Fn '" << CF.getName().str() << "' from "); if (CallerGraph->getReturnNodes().empty()) { DEBUG(errs() << "SYNTHESIZED INDIRECT GRAPH"); } else { DEBUG(errs() << "Fn '" << CS.getCallSite().getInstruction()-> getParent()->getParent()->getName().str() << "'"); } DEBUG(errs() << ": " << CF.getFunctionType()->getNumParams() << " args\n"); // Get the formal argument and return nodes for the called function and // merge them with the cloned subgraph. DSCallSite T1 = DSG->getCallSiteForArguments(CF); RC.mergeCallSite(T1, CS); ++NumTDInlines; EdgesFromCaller.pop_back(); } while (!EdgesFromCaller.empty() && EdgesFromCaller.back().CallerGraph == CallerGraph); } // Next, now that this graph is finalized, we need to recompute the // incompleteness markers for this graph and remove unreachable nodes. // If any of the functions is externally callable, treat everything in its // SCC as externally callable. bool isExternallyCallable = false; for (DSGraph::retnodes_iterator I = DSG->retnodes_begin(), E = DSG->retnodes_end(); I != E; ++I) if (ExternallyCallable.count(I->first)) { isExternallyCallable = true; break; } // Recompute the Incomplete markers. Depends on whether args are complete unsigned IncFlags = DSGraph::IgnoreFormalArgs; IncFlags |= DSGraph::IgnoreGlobals | DSGraph::MarkVAStart; DSG->markIncompleteNodes(IncFlags); // If this graph contains functions that are externally callable, now is the time to mark // their arguments and return values as external. At this point TD is inlining all caller information, // and that means External callers too. unsigned ExtFlags = isExternallyCallable ? DSGraph::MarkFormalsExternal : DSGraph::DontMarkFormalsExternal; DSG->computeExternalFlags(ExtFlags); DSG->computeIntPtrFlags(); cloneIntoGlobals(DSG, DSGraph::DontCloneCallNodes | DSGraph::DontCloneAuxCallNodes); // // Delete dead nodes. Treat globals that are unreachable as dead also. // // FIXME: // Do not delete unreachable globals as the comment describes. For its // alignment checks on the results of load instructions, SAFECode must be // able to find the DSNode of both the result of the load as well as the // pointer dereferenced by the load. If we remove unreachable globals, then // if the dereferenced pointer is a global, its DSNode will not reachable // from the local graph's scalar map, and chaos ensues. // // So, for now, just remove dead nodes but leave the globals alone. // DSG->removeDeadNodes(0); // We are done with computing the current TD Graph! Finally, before we can // finish processing this function, we figure out which functions it calls and // records these call graph edges, so that we have them when we process the // callee graphs. if (DSG->fc_begin() == DSG->fc_end()) return; // Loop over all the call sites and all the callees at each call site, and add // edges to the CallerEdges structure for each callee. for (DSGraph::fc_iterator CI = DSG->fc_begin(), E = DSG->fc_end(); CI != E; ++CI) { // Handle direct calls efficiently. if (CI->isDirectCall()) { if (!CI->getCalleeFunc()->isDeclaration() && !DSG->getReturnNodes().count(CI->getCalleeFunc())) CallerEdges[getOrCreateGraph(CI->getCalleeFunc())] .push_back(CallerCallEdge(DSG, &*CI, CI->getCalleeFunc())); continue; } svset<const Function*> AllCallees; std::vector<const Function*> Callees; // Get the list of callees callgraph.addFullFunctionSet(CI->getCallSite(), AllCallees); // Filter all non-declarations, and calls within this DSGraph for (svset<const Function*>::iterator I = AllCallees.begin(), E = AllCallees.end(); I != E; ++I) { const Function *F = *I; if (!F->isDeclaration() && getDSGraph(**I) != DSG) Callees.push_back(F); } AllCallees.clear(); // If there is exactly one callee from this call site, remember the edge in // CallerEdges. if (Callees.size() == 1) { const Function * Callee = Callees[0]; CallerEdges[getOrCreateGraph(Callee)] .push_back(CallerCallEdge(DSG, &*CI, Callee)); } if (Callees.size() <= 1) continue; // Otherwise, there are multiple callees from this call site, so it must be // an indirect call. Chances are that there will be other call sites with // this set of targets. If so, we don't want to do M*N inlining operations, // so we build up a new, private, graph that represents the calls of all // calls to this set of functions. std::map<std::vector<const Function*>, DSGraph*>::iterator IndCallRecI = IndCallMap.lower_bound(Callees); // If we already have this graph, recycle it. if (IndCallRecI != IndCallMap.end() && IndCallRecI->first == Callees) { DEBUG(errs() << " [TD] *** Reuse of indcall graph for " << Callees.size() << " callees!\n"); DSGraph * IndCallGraph = IndCallRecI->second; assert(IndCallGraph->getFunctionCalls().size() == 1); // Merge the call into the CS already in the IndCallGraph ReachabilityCloner RC(IndCallGraph, DSG, 0); RC.mergeCallSite(IndCallGraph->getFunctionCalls().front(), *CI); } else { // Otherwise, create a new DSGraph to represent this. DSGraph* IndCallGraph = new DSGraph(DSG->getGlobalECs(), DSG->getTargetData(), *TypeSS); // Clone over the call into the new DSGraph ReachabilityCloner RC(IndCallGraph, DSG, 0); DSCallSite ClonedCS = RC.cloneCallSite(*CI); // Add the cloned CS to the graph, as if it were an original call. IndCallGraph->getFunctionCalls().push_back(ClonedCS); // Save this graph for use later, should we need it. IndCallRecI = IndCallMap.insert(IndCallRecI, std::make_pair(Callees, IndCallGraph)); // Additionally, make sure that each of the callees inlines this graph // exactly once. DSCallSite *NCS = &IndCallGraph->getFunctionCalls().front(); for (unsigned i = 0, e = Callees.size(); i != e; ++i) { DSGraph* CalleeGraph = getDSGraph(*Callees[i]); if (CalleeGraph != DSG) CallerEdges[CalleeGraph].push_back(CallerCallEdge(IndCallGraph, NCS, Callees[i])); } } } }
/// InlineCallersIntoGraph - Inline all of the callers of the specified DS graph /// into it, then recompute completeness of nodes in the resultant graph. void TDDataStructures::InlineCallersIntoGraph(DSGraph* DSG) { // Inline caller graphs into this graph. First step, get the list of call // sites that call into this graph. std::vector<CallerCallEdge> EdgesFromCaller; std::map<DSGraph*, std::vector<CallerCallEdge> >::iterator CEI = CallerEdges.find(DSG); if (CEI != CallerEdges.end()) { std::swap(CEI->second, EdgesFromCaller); CallerEdges.erase(CEI); } // Sort the caller sites to provide a by-caller-graph ordering. std::sort(EdgesFromCaller.begin(), EdgesFromCaller.end()); // Merge information from the globals graph into this graph. FIXME: This is // stupid. Instead of us cloning information from the GG into this graph, // then having RemoveDeadNodes clone it back, we should do all of this as a // post-pass over all of the graphs. We need to take cloning out of // removeDeadNodes and gut removeDeadNodes at the same time first though. :( { DSGraph* GG = DSG->getGlobalsGraph(); ReachabilityCloner RC(DSG, GG, DSGraph::DontCloneCallNodes | DSGraph::DontCloneAuxCallNodes); for (DSScalarMap::global_iterator GI = DSG->getScalarMap().global_begin(), E = DSG->getScalarMap().global_end(); GI != E; ++GI) RC.getClonedNH(GG->getNodeForValue(*GI)); } DEBUG(errs() << "[TD] Inlining callers into '" << DSG->getFunctionNames() << "'\n"); // Iteratively inline caller graphs into this graph. while (!EdgesFromCaller.empty()) { DSGraph* CallerGraph = EdgesFromCaller.back().CallerGraph; // Iterate through all of the call sites of this graph, cloning and merging // any nodes required by the call. ReachabilityCloner RC(DSG, CallerGraph, DSGraph::DontCloneCallNodes | DSGraph::DontCloneAuxCallNodes); // Inline all call sites from this caller graph. do { const DSCallSite &CS = *EdgesFromCaller.back().CS; const Function &CF = *EdgesFromCaller.back().CalledFunction; DEBUG(errs() << " [TD] Inlining graph into Fn '" << CF.getNameStr() << "' from "); if (CallerGraph->getReturnNodes().empty()) { DEBUG(errs() << "SYNTHESIZED INDIRECT GRAPH"); } else { DEBUG(errs() << "Fn '" << CS.getCallSite().getInstruction()-> getParent()->getParent()->getNameStr() << "'"); } DEBUG(errs() << ": " << CF.getFunctionType()->getNumParams() << " args\n"); // Get the formal argument and return nodes for the called function and // merge them with the cloned subgraph. DSCallSite T1 = DSG->getCallSiteForArguments(CF); RC.mergeCallSite(T1, CS); ++NumTDInlines; EdgesFromCaller.pop_back(); } while (!EdgesFromCaller.empty() && EdgesFromCaller.back().CallerGraph == CallerGraph); } { DSGraph* GG = DSG->getGlobalsGraph(); ReachabilityCloner RC(GG, DSG, DSGraph::DontCloneCallNodes | DSGraph::DontCloneAuxCallNodes); for (DSScalarMap::global_iterator GI = DSG->getScalarMap().global_begin(), E = DSG->getScalarMap().global_end(); GI != E; ++GI) RC.getClonedNH(DSG->getNodeForValue(*GI)); } // Next, now that this graph is finalized, we need to recompute the // incompleteness markers for this graph and remove unreachable nodes. DSG->maskIncompleteMarkers(); // If any of the functions has incomplete incoming arguments, don't mark any // of them as complete. bool HasIncompleteArgs = false; for (DSGraph::retnodes_iterator I = DSG->retnodes_begin(), E = DSG->retnodes_end(); I != E; ++I) if (ArgsRemainIncomplete.count(I->first)) { HasIncompleteArgs = true; break; } // Recompute the Incomplete markers. Depends on whether args are complete unsigned Flags = HasIncompleteArgs ? DSGraph::MarkFormalArgs : DSGraph::IgnoreFormalArgs; Flags |= DSGraph::IgnoreGlobals | DSGraph::MarkVAStart; DSG->markIncompleteNodes(Flags); // Delete dead nodes. Treat globals that are unreachable as dead also. DSG->removeDeadNodes(DSGraph::RemoveUnreachableGlobals); // We are done with computing the current TD Graph! Finally, before we can // finish processing this function, we figure out which functions it calls and // records these call graph edges, so that we have them when we process the // callee graphs. if (DSG->fc_begin() == DSG->fc_end()) return; // Loop over all the call sites and all the callees at each call site, and add // edges to the CallerEdges structure for each callee. for (DSGraph::fc_iterator CI = DSG->fc_begin(), E = DSG->fc_end(); CI != E; ++CI) { // Handle direct calls efficiently. if (CI->isDirectCall()) { if (!CI->getCalleeFunc()->isDeclaration() && !DSG->getReturnNodes().count(CI->getCalleeFunc())) CallerEdges[getOrFetchDSGraph(CI->getCalleeFunc())] .push_back(CallerCallEdge(DSG, &*CI, CI->getCalleeFunc())); continue; } Instruction *CallI = CI->getCallSite().getInstruction(); // For each function in the invoked function list at this call site... calleeTy::iterator IPI = callee.begin(CallI), IPE = callee.end(CallI); // Skip over all calls to this graph (SCC calls). while (IPI != IPE && getDSGraph(*IPI) == DSG) ++IPI; // All SCC calls? if (IPI == IPE) continue; const Function *FirstCallee = *IPI; ++IPI; // Skip over more SCC calls. while (IPI != IPE && getDSGraph(*IPI) == DSG) ++IPI; // If there is exactly one callee from this call site, remember the edge in // CallerEdges. if (IPI == IPE) { if (!FirstCallee->isDeclaration()) CallerEdges[getOrFetchDSGraph(FirstCallee)] .push_back(CallerCallEdge(DSG, &*CI, FirstCallee)); continue; } // Otherwise, there are multiple callees from this call site, so it must be // an indirect call. Chances are that there will be other call sites with // this set of targets. If so, we don't want to do M*N inlining operations, // so we build up a new, private, graph that represents the calls of all // calls to this set of functions. std::vector<const Function*> Callees; for (calleeTy::iterator I = callee.begin(CallI), E = callee.end(CallI); I != E; ++I) if (!(*I)->isDeclaration()) Callees.push_back(*I); std::sort(Callees.begin(), Callees.end()); std::map<std::vector<const Function*>, DSGraph*>::iterator IndCallRecI = IndCallMap.lower_bound(Callees); DSGraph *IndCallGraph; // If we already have this graph, recycle it. if (IndCallRecI != IndCallMap.end() && IndCallRecI->first == Callees) { DEBUG(errs() << " [TD] *** Reuse of indcall graph for " << Callees.size() << " callees!\n"); IndCallGraph = IndCallRecI->second; } else { // Otherwise, create a new DSGraph to represent this. IndCallGraph = new DSGraph(DSG->getGlobalECs(), DSG->getTargetData(), GlobalsGraph); // Make a nullary dummy call site, which will eventually get some content // merged into it. The actual callee function doesn't matter here, so we // just pass it something to keep the ctor happy. std::vector<DSNodeHandle> ArgDummyVec; DSCallSite DummyCS(CI->getCallSite(), DSNodeHandle(), Callees[0]/*dummy*/, ArgDummyVec); IndCallGraph->getFunctionCalls().push_back(DummyCS); IndCallRecI = IndCallMap.insert(IndCallRecI, std::make_pair(Callees, IndCallGraph)); // Additionally, make sure that each of the callees inlines this graph // exactly once. DSCallSite *NCS = &IndCallGraph->getFunctionCalls().front(); for (unsigned i = 0, e = Callees.size(); i != e; ++i) { DSGraph* CalleeGraph = getDSGraph(Callees[i]); if (CalleeGraph != DSG) CallerEdges[CalleeGraph].push_back(CallerCallEdge(IndCallGraph, NCS, Callees[i])); } } // Now that we know which graph to use for this, merge the caller // information into the graph, based on information from the call site. ReachabilityCloner RC(IndCallGraph, DSG, 0); RC.mergeCallSite(IndCallGraph->getFunctionCalls().front(), *CI); } }