// // Function: getAllCallees() // // Description: // Given a DSCallSite, add to the list the functions that can be called by // the call site *if* it is resolvable. Uses 'applyCallsiteFilter' to // only add the functions that are valid targets of this callsite. // void BUDataStructures:: getAllCallees(const DSCallSite &CS, FuncSet &Callees) { // // FIXME: Should we check for the Unknown flag on indirect call sites? // // Direct calls to functions that have bodies are always resolvable. // Indirect function calls that are for a complete call site (the analysis // knows everything about the call site) and do not target external functions // are also resolvable. // if (CS.isDirectCall()) { if (!CS.getCalleeFunc()->isDeclaration()) Callees.insert(CS.getCalleeFunc()); } else if (CS.getCalleeNode()->isCompleteNode()) { // Get all callees. if (!CS.getCalleeNode()->isExternFuncNode()) { // Get all the callees for this callsite FuncSet TempCallees; CS.getCalleeNode()->addFullFunctionSet(TempCallees); // Filter out the ones that are invalid targets with respect // to this particular callsite. applyCallsiteFilter(CS, TempCallees); // Insert the remaining callees (legal ones, if we're filtering) // into the master 'Callees' list Callees.insert(TempCallees.begin(), TempCallees.end()); } } }
/** * @brief Tries to find functions that can be called by indirect call. * * @par Preconditions * - @a callInst is a call that calls some function indirectly. * * @param[in] call We try to find functions for this indirect call. * @param[in] funcsToCheck We are finding functions that can be indirectly called * only in this functions. * * @return Found functions that can be called indirectly. */ FuncSet IndirectlyCalledFuncsAnalysis::getFuncsForIndirectCall( const CallInst &call, const FuncVec &funcsToCheck) { assert(isIndirectCall(call) && "Expected an indirect call."); FuncSet result; Type *callReturnType = call.getType(); for (Function *func : funcsToCheck) { if (func->getReturnType() != callReturnType) { continue; } if (!func->isVarArg()) { if (!hasEqArgsAndParams(call, *func)) { continue; } } result.insert(func); } return result; }
// // Function: applyCallsiteFilter // // Description: // Given a DSCallSite, and a list of functions, filter out the ones // that aren't callable from the given Callsite. // // Does no filtering if 'filterCallees' is set to false. // void BUDataStructures:: applyCallsiteFilter(const DSCallSite &DCS, FuncSet &Callees) { if (!filterCallees) return; FuncSet::iterator I = Callees.begin(); CallSite CS = DCS.getCallSite(); while (I != Callees.end()) { if (functionIsCallable(CS, *I)) { ++I; } else { I = Callees.erase(I); } } }
/** * @brief Visits the given node in the call graph. * * @param[in] calledFunc The given node. * @param[in,out] calledFuncInfo Information about @a calledFunc. * * Corresponds to the strongconnect(v) function from * http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm */ void CallInfoObtainer::SCCComputer::visit(ShPtr<CG::CalledFuncs> calledFunc, CalledFuncInfo &calledFuncInfo) { // Set the depth index for calledFunc to the smallest unused index. calledFuncInfo.index = calledFuncInfo.lowlink = index; index++; // Push calledFunc onto the stack. stack.push(calledFunc); calledFuncInfo.onStack = true; // Consider the successors of calledFunc. for (const auto &callee : calledFunc->callees) { ShPtr<CG::CalledFuncs> succ(cg->getCalledFuncs(callee)); CalledFuncInfo &succInfo(calledFuncInfoMap[succ]); if (succInfo.index < 0) { // '< 0' means 'undefined' // The successor has not yet been visited; recurse on it. visit(succ, succInfo); calledFuncInfo.lowlink = std::min(calledFuncInfo.lowlink, succInfo.lowlink); } else if (succInfo.onStack) { // The successor is on the stack and hence in the current SCC. calledFuncInfo.lowlink = std::min(calledFuncInfo.lowlink, succInfo.index); } } // If calledFunc is a root node, pop the stack and generate an SCC. if (calledFuncInfo.lowlink == calledFuncInfo.index) { // Generate a new SCC. FuncSet scc; ShPtr<CG::CalledFuncs> poppedCalledFunc; do { poppedCalledFunc = stack.top(); stack.pop(); calledFuncInfoMap[cg->getCalledFuncs( poppedCalledFunc->caller)].onStack = false; scc.insert(poppedCalledFunc->caller); } while (calledFunc != poppedCalledFunc); // Store the generated SCC. However, if the SCC contains just a single // function, do this only if it calls itself (see the description of // computeSCCs()). if (scc.size() != 1 || hasItem(calledFunc->callees, calledFunc->caller)) { sccs.insert(scc); } } }
// // Function: getAllAuxCallees() // // Description: // Return a list containing all of the resolvable callees in the auxiliary // list for the specified graph in the Callees vector. // // Inputs: // G - The DSGraph for which the callers wants a list of resolvable call // sites. // // Outputs: // Callees - A list of all functions that can be called from resolvable call // sites. This list is always cleared by this function before any // functions are added to it. // void BUDataStructures:: getAllAuxCallees (DSGraph* G, FuncSet & Callees) { // // Clear out the list of callees. // Callees.clear(); for (DSGraph::afc_iterator I = G->afc_begin(), E = G->afc_end(); I != E; ++I) getAllCallees(*I, Callees); }
void DSCallGraph::buildRoots() { FuncSet knownCallees; FuncSet knownCallers; for (SimpleCalleesTy::iterator ii = SimpleCallees.begin(), ee = SimpleCallees.end(); ii != ee; ++ii) { knownCallees.insert(ii->second.begin(), ii->second.end()); knownCallers.insert(ii->first); } knownRoots.clear(); std::set_difference(knownCallers.begin(), knownCallers.end(), knownCallees.begin(), knownCallees.end(), std::inserter(knownRoots, knownRoots.begin())); }
/** * @brief Finds a next SCC and its represent and returns them. * * @param[in] sccs All SCCs in the call graph. * @param[in] computedFuncs Functions that already have been included in * FuncInfoCompOrder::order. * @param[in] remainingFuncs Functions that haven't been included in * FuncInfoCompOrder::order. * * @par Preconditions * - @a remainingFuncs is non-empty * - @a remainingFuncs doesn't contain a function which calls just functions * from @a computedFuncs. */ CallInfoObtainer::SCCWithRepresent CallInfoObtainer::findNextSCC(const FuncSetSet &sccs, const FuncSet &computedFuncs, const FuncSet &remainingFuncs) const { PRECONDITION(!remainingFuncs.empty(), "it should not be empty"); // // We try to locate an SCC whose members call just the functions in // the SCC or in computedFuncs. Then, if the found SCC contains a function // from remainingFuncs, return the function. // // For every SCC... for (const auto &scc : sccs) { bool sccFound = true; ShPtr<Function> funcFromRemainingFuncs; // For every function in the SCC... for (const auto &func : scc) { // Check whether the function calls just the functions in the SCC // or in computedFuncs. ShPtr<CG::CalledFuncs> calledFuncs(cg->getCalledFuncs(func)); FuncSet mayCall(setUnion(scc, computedFuncs)); if (!setDifference(calledFuncs->callees, mayCall).empty()) { sccFound = false; } else { // Have we encountered a function from remainingFuncs? if (hasItem(remainingFuncs, func)) { funcFromRemainingFuncs = func; } } } if (sccFound && funcFromRemainingFuncs) { return SCCWithRepresent(scc, funcFromRemainingFuncs); } } // TODO Can this happen? printWarningMessage("[SCCComputer] No viable SCC has been found."); FuncSet scc; ShPtr<Function> func(*(remainingFuncs.begin())); scc.insert(func); return SCCWithRepresent(scc, func); }
// // Method: calculateGraphs() // // Description: // Perform recursive bottom-up inlining of DSGraphs from callee to caller. // // Inputs: // F - The function which should have its callees' DSGraphs merged into its // own DSGraph. // Stack - The stack used for Tarjan's SCC-finding algorithm. // NextID - The nextID value used for Tarjan's SCC-finding algorithm. // ValMap - The map used for Tarjan's SCC-finding algorithm. // // Return value: // unsigned BUDataStructures::calculateGraphs (const Function *F, TarjanStack & Stack, unsigned & NextID, TarjanMap & ValMap) { assert(!ValMap.count(F) && "Shouldn't revisit functions!"); unsigned Min = NextID++, MyID = Min; ValMap[F] = Min; Stack.push_back(F); // // FIXME: This test should be generalized to be any function that we have // already processed in the case when there isn't a main() or there are // unreachable functions! // if (F->isDeclaration()) { // sprintf, fprintf, sscanf, etc... // No callees! Stack.pop_back(); ValMap[F] = ~0; return Min; } // // Get the DSGraph of the current function. Make one if one doesn't exist. // DSGraph* Graph = getOrCreateGraph(F); // // Find all callee functions. Use the DSGraph for this (do not use the call // graph (DSCallgraph) as we're still in the process of constructing it). // FuncSet CalleeFunctions; getAllAuxCallees(Graph, CalleeFunctions); // // Iterate through each call target (these are the edges out of the current // node (i.e., the current function) in Tarjan graph parlance). Find the // minimum assigned ID. // for (FuncSet::iterator I = CalleeFunctions.begin(), E = CalleeFunctions.end(); I != E; ++I) { const Function *Callee = *I; unsigned M; // // If we have not visited this callee before, visit it now (this is the // post-order component of the Bottom-Up algorithm). Otherwise, look up // the assigned ID value from the Tarjan Value Map. // TarjanMap::iterator It = ValMap.find(Callee); if (It == ValMap.end()) // No, visit it now. M = calculateGraphs(Callee, Stack, NextID, ValMap); else // Yes, get it's number. M = It->second; // // If we've found a function with a smaller ID than this funtion, record // that ID as the minimum ID. // if (M < Min) Min = M; } assert(ValMap[F] == MyID && "SCC construction assumption wrong!"); // // If the minimum ID found is not this function's ID, then this function is // part of a larger SCC. // if (Min != MyID) return Min; // // If this is a new SCC, process it now. // if (Stack.back() == F) { // Special case the single "SCC" case here. DEBUG(errs() << "Visiting single node SCC #: " << MyID << " fn: " << F->getName() << "\n"); Stack.pop_back(); DEBUG(errs() << " [BU] Calculating graph for: " << F->getName()<< "\n"); DSGraph* G = getOrCreateGraph(F); calculateGraph(G); DEBUG(errs() << " [BU] Done inlining: " << F->getName() << " [" << G->getGraphSize() << "+" << G->getAuxFunctionCalls().size() << "]\n"); if (MaxSCC < 1) MaxSCC = 1; // // Should we revisit the graph? Only do it if there are now new resolvable // callees. FuncSet NewCallees; getAllAuxCallees(G, NewCallees); if (!NewCallees.empty()) { if (hasNewCallees(NewCallees, CalleeFunctions)) { DEBUG(errs() << "Recalculating " << F->getName() << " due to new knowledge\n"); ValMap.erase(F); ++NumRecalculations; return calculateGraphs(F, Stack, NextID, ValMap); } ++NumRecalculationsSkipped; } ValMap[F] = ~0U; return MyID; } else { unsigned SCCSize = 1; const Function *NF = Stack.back(); if(NF != F) ValMap[NF] = ~0U; DSGraph* SCCGraph = getDSGraph(*NF); // // First thing first: collapse all of the DSGraphs into a single graph for // the entire SCC. Splice all of the graphs into one and discard all of // the old graphs. // while (NF != F) { Stack.pop_back(); NF = Stack.back(); if(NF != F) ValMap[NF] = ~0U; DSGraph* NFG = getDSGraph(*NF); if (NFG != SCCGraph) { // Update the Function -> DSG map. for (DSGraph::retnodes_iterator I = NFG->retnodes_begin(), E = NFG->retnodes_end(); I != E; ++I) setDSGraph(*I->first, SCCGraph); SCCGraph->spliceFrom(NFG); delete NFG; ++SCCSize; } } Stack.pop_back(); DEBUG(errs() << "Calculating graph for SCC #: " << MyID << " of size: " << SCCSize << "\n"); // Compute the Max SCC Size. if (MaxSCC < SCCSize) MaxSCC = SCCSize; // Clean up the graph before we start inlining a bunch again... SCCGraph->removeDeadNodes(DSGraph::KeepUnreachableGlobals); // Now that we have one big happy family, resolve all of the call sites in // the graph... calculateGraph(SCCGraph); DEBUG(errs() << " [BU] Done inlining SCC [" << SCCGraph->getGraphSize() << "+" << SCCGraph->getAuxFunctionCalls().size() << "]\n" << "DONE with SCC #: " << MyID << "\n"); FuncSet NewCallees; getAllAuxCallees(SCCGraph, NewCallees); if (!NewCallees.empty()) { if (hasNewCallees(NewCallees, CalleeFunctions)) { DEBUG(errs() << "Recalculating SCC Graph " << F->getName() << " due to new knowledge\n"); ValMap.erase(F); ++NumRecalculations; return calculateGraphs(F, Stack, NextID, ValMap); } ++NumRecalculationsSkipped; } ValMap[F] = ~0U; return MyID; } }