blob: 98ade51b8e32eedfd212caddd4c138e16fdc9d55 [file] [log] [blame]
//===- BottomUpClosure.cpp - Compute bottom-up interprocedural closure ----===//
//
// The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// 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.
//
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "dsa-bu"
#include "llvm/IR/Constants.h"
#include "dsa/DataStructure.h"
#include "dsa/DSGraph.h"
#include "llvm/IR/Module.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/FormattedStream.h"
using namespace llvm;
namespace {
STATISTIC (MaxSCC, "Maximum SCC Size in Call Graph");
STATISTIC (NumInlines, "Number of graphs inlined");
STATISTIC (NumCallEdges, "Number of 'actual' call edges");
STATISTIC (NumIndResolved, "Number of resolved IndCalls");
STATISTIC (NumIndUnresolved, "Number of unresolved IndCalls");
// NumEmptyCalls = NumIndUnresolved + Number of calls to external functions
STATISTIC (NumEmptyCalls, "Number of calls we know nothing about");
STATISTIC (NumRecalculations, "Number of DSGraph recalculations");
STATISTIC (NumRecalculationsSkipped, "Number of DSGraph recalculations skipped");
RegisterPass<BUDataStructures>
X("dsa-bu", "Bottom-up Data Structure Analysis");
}
char BUDataStructures::ID;
// run - Calculate the bottom up data structure graphs for each function in the
// program.
//
bool BUDataStructures::runOnModule(Module &M) {
init(&getAnalysis<StdLibDataStructures>(), true, true, false, false );
return runOnModuleInternal(M);
}
// BU:
// Construct the callgraph from the local graphs
// Find SCCs
// inline bottom up
//
bool BUDataStructures::runOnModuleInternal(Module& M) {
//
// Make sure we have a DSGraph for all declared functions in the Module.
// While we may not need them in this DSA pass, a later DSA pass may ask us
// for their DSGraphs, and we want to have them if asked.
//
for (Module::iterator F = M.begin(); F != M.end(); ++F) {
if (!(F->isDeclaration())){
getOrCreateGraph(F);
}
}
//
// Do a post-order traversal of the SCC callgraph and do bottom-up inlining.
//
postOrderInline (M);
// At the end of the bottom-up pass, the globals graph becomes complete.
// FIXME: This is not the right way to do this, but it is sorta better than
// nothing! In particular, externally visible globals and unresolvable call
// nodes at the end of the BU phase should make things that they point to
// incomplete in the globals graph.
//
GlobalsGraph->removeTriviallyDeadNodes();
GlobalsGraph->maskIncompleteMarkers();
// Mark external globals incomplete.
GlobalsGraph->markIncompleteNodes(DSGraph::IgnoreGlobals);
GlobalsGraph->computeExternalFlags(DSGraph::DontMarkFormalsExternal);
GlobalsGraph->computeIntPtrFlags();
//
// Create equivalence classes for aliasing globals so that we only need to
// record one global per DSNode.
//
formGlobalECs();
// Merge the globals variables (not the calls) from the globals graph back
// into the individual function's graph so that changes made to globals during
// BU can be reflected. This is specifically needed for correct call graph
//
for (Module::iterator F = M.begin(); F != M.end(); ++F) {
if (!(F->isDeclaration())){
DSGraph *Graph = getOrCreateGraph(F);
cloneGlobalsInto(Graph, DSGraph::DontCloneCallNodes |
DSGraph::DontCloneAuxCallNodes);
Graph->buildCallGraph(callgraph, GlobalFunctionList, filterCallees);
Graph->maskIncompleteMarkers();
Graph->markIncompleteNodes(DSGraph::MarkFormalArgs |
DSGraph::IgnoreGlobals);
Graph->computeExternalFlags(DSGraph::DontMarkFormalsExternal);
Graph->computeIntPtrFlags();
}
}
// Once the correct flags have been calculated. Update the callgraph.
for (Module::iterator F = M.begin(); F != M.end(); ++F) {
if (!(F->isDeclaration())){
DSGraph *Graph = getOrCreateGraph(F);
Graph->buildCompleteCallGraph(callgraph,
GlobalFunctionList, filterCallees);
}
}
NumCallEdges += callgraph.size();
// Put the call graph in canonical form
callgraph.buildSCCs();
callgraph.buildRoots();
return false;
}
//
// 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);
}
}
}
//
// 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());
}
}
}
//
// 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);
}
//
// Method: postOrderInline()
//
// Description:
// This methods does a post order traversal of the call graph and performs
// bottom-up inlining of the DSGraphs.
//
void
BUDataStructures::postOrderInline (Module & M) {
// Variables used for Tarjan SCC-finding algorithm. These are passed into
// the recursive function used to find SCCs.
std::vector<const Function*> Stack;
std::map<const Function*, unsigned> ValMap;
unsigned NextID = 1;
// Do post order traversal on the global ctors. Use this information to update
// the globals graph.
const char *Name = "llvm.global_ctors";
GlobalVariable *GV = M.getNamedGlobal(Name);
if (GV && !(GV->isDeclaration()) && !(GV->hasLocalLinkage())) {
// Should be an array of '{ int, void ()* }' structs. The first value is
// the init priority, which we ignore.
ConstantArray *InitList = dyn_cast<ConstantArray>(GV->getInitializer());
if (InitList) {
for (unsigned i = 0, e = InitList->getNumOperands(); i != e; ++i)
if (ConstantStruct *CS = dyn_cast<ConstantStruct>(InitList->getOperand(i))) {
if (CS->getNumOperands() != 2)
break; // Not array of 2-element structs.
Constant *FP = CS->getOperand(1);
if (FP->isNullValue())
break; // Found a null terminator, exit.
if (ConstantExpr *CE = dyn_cast<ConstantExpr>(FP))
if (CE->isCast())
FP = CE->getOperand(0);
Function *F = dyn_cast<Function>(FP);
if (F && !F->isDeclaration() && !ValMap.count(F)) {
calculateGraphs(F, Stack, NextID, ValMap);
CloneAuxIntoGlobal(getDSGraph(*F));
}
}
GlobalsGraph->removeTriviallyDeadNodes();
GlobalsGraph->maskIncompleteMarkers();
// Mark external globals incomplete.
GlobalsGraph->markIncompleteNodes(DSGraph::IgnoreGlobals);
GlobalsGraph->computeExternalFlags(DSGraph::DontMarkFormalsExternal);
GlobalsGraph->computeIntPtrFlags();
//
// Create equivalence classes for aliasing globals so that we only need to
// record one global per DSNode.
//
formGlobalECs();
// propogte information calculated
// from the globals graph to the other graphs.
for (Module::iterator F = M.begin(); F != M.end(); ++F) {
if (!(F->isDeclaration())){
DSGraph *Graph = getDSGraph(*F);
cloneGlobalsInto(Graph, DSGraph::DontCloneCallNodes |
DSGraph::DontCloneAuxCallNodes);
Graph->buildCallGraph(callgraph, GlobalFunctionList, filterCallees);
Graph->maskIncompleteMarkers();
Graph->markIncompleteNodes(DSGraph::MarkFormalArgs |
DSGraph::IgnoreGlobals);
Graph->computeExternalFlags(DSGraph::DontMarkFormalsExternal);
Graph->computeIntPtrFlags();
}
}
}
}
//
// Start the post order traversal with the main() function. If there is no
// main() function, don't worry; we'll have a separate traversal for inlining
// graphs for functions not reachable from main().
//
Function *MainFunc = M.getFunction ("main");
if (MainFunc && !MainFunc->isDeclaration()) {
calculateGraphs(MainFunc, Stack, NextID, ValMap);
CloneAuxIntoGlobal(getDSGraph(*MainFunc));
}
//
// Calculate the graphs for any functions that are unreachable from main...
//
for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I)
if (!I->isDeclaration() && !ValMap.count(I)) {
if (MainFunc)
DEBUG(errs() << debugname << ": Function unreachable from main: "
<< I->getName() << "\n");
calculateGraphs(I, Stack, NextID, ValMap); // Calculate all graphs.
CloneAuxIntoGlobal(getDSGraph(*I));
// Mark this graph as processed. Do this by finding all functions
// in the graph that map to it, and mark them visited.
// Note that this really should be handled neatly by calculateGraphs
// itself, not here. However this catches the worst offenders.
DSGraph *G = getDSGraph(*I);
for(DSGraph::retnodes_iterator RI = G->retnodes_begin(),
RE = G->retnodes_end(); RI != RE; ++RI) {
if (getDSGraph(*RI->first) == G) {
if (!ValMap.count(RI->first))
ValMap[RI->first] = ~0U;
else
assert(ValMap[RI->first] == ~0U);
}
}
}
return;
}
static bool hasNewCallees(svset<const Function*> &New,
svset<const Function*> &Old) {
if (New.size() > Old.size()) return true;
svset<const Function*>::iterator NI = New.begin(), NE = New.end();
for (; NI != NE; ++NI)
if (!Old.count(*NI)) return true;
return false;
}
//
// 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;
}
}
//
// Method: CloneAuxIntoGlobal()
//
// Description:
// This method takes the specified graph and processes each unresolved call
// site (a call site for which all targets are not yet known). For each
// unresolved call site, it adds it to the globals graph and merges
// information about the call site if the globals graph already had the call
// site in its own list of unresolved call sites.
//
void BUDataStructures::CloneAuxIntoGlobal(DSGraph* G) {
//
// If this DSGraph has no unresolved call sites, do nothing. We do enough
// work that wastes time even when the list is empty that this extra check
// is probably worth it.
//
if (G->afc_begin() == G->afc_end())
return;
DSGraph* GG = G->getGlobalsGraph();
ReachabilityCloner RC(GG, G, 0);
//
// Determine which called values are both within the local graph DSCallsites
// and the global graph DSCallsites. Note that we require that the global
// graph have a DSNode for the called value.
//
std::map<Value *, DSCallSite *> CommonCallValues;
for (DSGraph::afc_iterator ii = G->afc_begin(), ee = G->afc_end();
ii != ee;
++ii) {
//
// If the globals graph has a DSNode for the LLVM value used in the local
// unresolved call site, then it might have a DSCallSite for it, too.
// Record this call site as a potential call site that will need to be
// merged.
//
// Otherwise, just add the call site to the globals graph.
//
Value * V = ii->getCallSite().getCalledValue();
if (GG->hasNodeForValue(V)) {
DSCallSite & DS = *ii;
CommonCallValues[V] = &DS;
} else {
GG->addAuxFunctionCall(RC.cloneCallSite(*ii));
}
}
//
// Scan through all the unresolved call sites in the globals graph and see if
// the local graph has a call using the same LLVM value. If so, merge the
// call sites.
//
DSGraph::afc_iterator GGii = GG->afc_begin();
for (; GGii != GG->afc_end(); ++GGii) {
//
// Determine if this unresolved call site is also in the local graph.
// If so, then merge it.
//
Value * CalledValue = GGii->getCallSite().getCalledValue();
std::map<Value *, DSCallSite *>::iterator v;
v = CommonCallValues.find (CalledValue);
if (v != CommonCallValues.end()) {
//
// Merge the unresolved call site into the globals graph.
//
RC.cloneCallSite(*(v->second)).mergeWith(*GGii);
//
// Mark that this call site was merged by removing the called LLVM value
// from the set of values common to both the local and global DSGraphs.
//
CommonCallValues.erase (v);
}
}
//
// We've now merged all DSCallSites that were known both to the local graph
// and the globals graph. Now, there are still some local call sites that
// need to be *added* to the globals graph; they are in DSCallSites remaining
// in CommonCallValues.
//
std::map<Value *, DSCallSite *>::iterator v = CommonCallValues.begin ();
for (; v != CommonCallValues.end(); ++v) {
GG->addAuxFunctionCall(RC.cloneCallSite(*(v->second)));
}
return;
}
//
// Description:
// Inline all graphs in the callgraph and remove callsites that are completely
// dealt with
//
void BUDataStructures::calculateGraph(DSGraph* Graph) {
DEBUG(Graph->AssertGraphOK(); Graph->getGlobalsGraph()->AssertGraphOK());
Graph->buildCallGraph(callgraph, GlobalFunctionList, filterCallees);
// Move our call site list into TempFCs so that inline call sites go into the
// new call site list and doesn't invalidate our iterators!
DSGraph::FunctionListTy TempFCs;
DSGraph::FunctionListTy &AuxCallsList = Graph->getAuxFunctionCalls();
TempFCs.swap(AuxCallsList);
for (auto &CS : TempFCs) {
DEBUG(Graph->AssertGraphOK(); Graph->getGlobalsGraph()->AssertGraphOK());
// Fast path for noop calls. Note that we don't care about merging globals
// in the callee with nodes in the caller here.
if (!CS.isIndirectCall() && CS.getRetVal().isNull()
&& CS.getNumPtrArgs() == 0 && !CS.isVarArg()) {
continue;
}
// If this callsite is unresolvable, get rid of it now.
if (CS.isUnresolvable()) {
continue;
}
// Find all callees for this callsite, according to the DSGraph!
// Do *not* use the callgraph, because we're updating that as we go!
FuncSet CalledFuncs;
getAllCallees(CS,CalledFuncs);
if (CalledFuncs.empty()) {
++NumEmptyCalls;
if (CS.isIndirectCall())
++NumIndUnresolved;
// Remember that we could not resolve this yet!
AuxCallsList.push_back(CS);
continue;
}
// If we get to this point, we know the callees, and can inline.
// This means, that either it is a direct call site. Or if it is
// an indirect call site, its calleeNode is complete, and we can
// resolve this particular call site.
assert((CS.isDirectCall() || CS.getCalleeNode()->isCompleteNode())
&& "Resolving an indirect incomplete call site");
if (CS.isIndirectCall()) {
++NumIndResolved;
}
DSGraph *GI;
for (auto *Callee : CalledFuncs) {
// Get the data structure graph for the called function.
GI = getDSGraph(*Callee); // Graph to inline
DEBUG(GI->AssertGraphOK(); GI->getGlobalsGraph()->AssertGraphOK());
DEBUG(errs() << " Inlining graph for " << Callee->getName()
<< "[" << GI->getGraphSize() << "+"
<< GI->getAuxFunctionCalls().size() << "] into '"
<< Graph->getFunctionNames() << "' [" << Graph->getGraphSize() <<"+"
<< Graph->getAuxFunctionCalls().size() << "]\n");
//
// Merge in the DSGraph of the called function.
//
// TODO:
// Why are the strip alloca bit and don't clone call nodes bit set?
//
// I believe the answer is on page 6 of the PLDI paper on DSA. The
// idea is that stack objects are invalid if they escape.
//
Graph->mergeInGraph(CS, *Callee, *GI,
DSGraph::StripAllocaBit|DSGraph::DontCloneCallNodes);
++NumInlines;
DEBUG(Graph->AssertGraphOK(););
}
}
TempFCs.clear();
// Recompute the Incomplete markers
Graph->maskIncompleteMarkers();
Graph->markIncompleteNodes(DSGraph::MarkFormalArgs);
Graph->computeExternalFlags(DSGraph::DontMarkFormalsExternal);
Graph->computeIntPtrFlags();
//
// Update the callgraph with the new information that we have gleaned.
// NOTE : This must be called before removeDeadNodes, so that no
// information is lost due to deletion of DSCallNodes.
Graph->buildCallGraph(callgraph, GlobalFunctionList, filterCallees);
// Delete dead nodes. Treat globals that are unreachable but that can
// reach live nodes as live.
Graph->removeDeadNodes(DSGraph::KeepUnreachableGlobals);
cloneIntoGlobals(Graph, DSGraph::DontCloneCallNodes |
DSGraph::DontCloneAuxCallNodes |
DSGraph::StripAllocaBit);
//Graph->writeGraphToFile(cerr, "bu_" + F.getName());
}