| //===-- PoolAllocate.cpp - Pool Allocation Pass ---------------------------===// |
| // |
| // This transform changes programs so that disjoint data structures are |
| // allocated out of different pools of memory, increasing locality. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/Transforms/PoolAllocate.h" |
| #include "llvm/Transforms/Utils/Cloning.h" |
| #include "llvm/Analysis/DataStructure.h" |
| #include "llvm/Analysis/DSGraph.h" |
| #include "llvm/Module.h" |
| #include "llvm/DerivedTypes.h" |
| #include "llvm/Constants.h" |
| #include "llvm/Instructions.h" |
| #include "llvm/Target/TargetData.h" |
| #include "llvm/Support/InstVisitor.h" |
| #include "Support/Statistic.h" |
| #include "Support/VectorExtras.h" |
| |
| using namespace PA; |
| |
| namespace { |
| const Type *VoidPtrTy = PointerType::get(Type::SByteTy); |
| // The type to allocate for a pool descriptor: { sbyte*, uint } |
| const Type *PoolDescType = |
| StructType::get(make_vector<const Type*>(VoidPtrTy, Type::UIntTy, 0)); |
| const PointerType *PoolDescPtr = PointerType::get(PoolDescType); |
| |
| RegisterOpt<PoolAllocate> |
| X("poolalloc", "Pool allocate disjoint data structures"); |
| } |
| |
| void PoolAllocate::getAnalysisUsage(AnalysisUsage &AU) const { |
| AU.addRequired<BUDataStructures>(); |
| AU.addRequired<TargetData>(); |
| } |
| |
| bool PoolAllocate::run(Module &M) { |
| if (M.begin() == M.end()) return false; |
| CurModule = &M; |
| |
| AddPoolPrototypes(); |
| BU = &getAnalysis<BUDataStructures>(); |
| |
| std::map<Function*, Function*> FuncMap; |
| |
| // Loop over only the function initially in the program, don't traverse newly |
| // added ones. If the function uses memory, make its clone. |
| Module::iterator LastOrigFunction = --M.end(); |
| for (Module::iterator I = M.begin(); ; ++I) { |
| if (!I->isExternal()) |
| if (Function *R = MakeFunctionClone(*I)) |
| FuncMap[I] = R; |
| if (I == LastOrigFunction) break; |
| } |
| |
| ++LastOrigFunction; |
| |
| // Now that all call targets are available, rewrite the function bodies of the |
| // clones. |
| for (Module::iterator I = M.begin(); I != LastOrigFunction; ++I) |
| if (!I->isExternal()) { |
| std::map<Function*, Function*>::iterator FI = FuncMap.find(I); |
| ProcessFunctionBody(*I, FI != FuncMap.end() ? *FI->second : *I); |
| } |
| |
| FunctionInfo.clear(); |
| return true; |
| } |
| |
| |
| // AddPoolPrototypes - Add prototypes for the pool functions to the specified |
| // module and update the Pool* instance variables to point to them. |
| // |
| void PoolAllocate::AddPoolPrototypes() { |
| CurModule->addTypeName("PoolDescriptor", PoolDescType); |
| |
| // Get poolinit function... |
| FunctionType *PoolInitTy = |
| FunctionType::get(Type::VoidTy, |
| make_vector<const Type*>(PoolDescPtr, Type::UIntTy, 0), |
| false); |
| PoolInit = CurModule->getOrInsertFunction("poolinit", PoolInitTy); |
| |
| // Get pooldestroy function... |
| std::vector<const Type*> PDArgs(1, PoolDescPtr); |
| FunctionType *PoolDestroyTy = |
| FunctionType::get(Type::VoidTy, PDArgs, false); |
| PoolDestroy = CurModule->getOrInsertFunction("pooldestroy", PoolDestroyTy); |
| |
| // Get the poolalloc function... |
| FunctionType *PoolAllocTy = FunctionType::get(VoidPtrTy, PDArgs, false); |
| PoolAlloc = CurModule->getOrInsertFunction("poolalloc", PoolAllocTy); |
| |
| // Get the poolfree function... |
| PDArgs.push_back(VoidPtrTy); // Pointer to free |
| FunctionType *PoolFreeTy = FunctionType::get(Type::VoidTy, PDArgs, false); |
| PoolFree = CurModule->getOrInsertFunction("poolfree", PoolFreeTy); |
| |
| #if 0 |
| Args[0] = Type::UIntTy; // Number of slots to allocate |
| FunctionType *PoolAllocArrayTy = FunctionType::get(VoidPtrTy, Args, true); |
| PoolAllocArray = CurModule->getOrInsertFunction("poolallocarray", |
| PoolAllocArrayTy); |
| #endif |
| } |
| |
| |
| // MakeFunctionClone - If the specified function needs to be modified for pool |
| // allocation support, make a clone of it, adding additional arguments as |
| // neccesary, and return it. If not, just return null. |
| // |
| Function *PoolAllocate::MakeFunctionClone(Function &F) { |
| DSGraph &G = BU->getDSGraph(F); |
| std::vector<DSNode*> &Nodes = G.getNodes(); |
| if (Nodes.empty()) return 0; // No memory activity, nothing is required |
| |
| FuncInfo &FI = FunctionInfo[&F]; // Create a new entry for F |
| FI.Clone = 0; |
| |
| // Find DataStructure nodes which are allocated in pools non-local to the |
| // current function. This set will contain all of the DSNodes which require |
| // pools to be passed in from outside of the function. |
| hash_set<DSNode*> &MarkedNodes = FI.MarkedNodes; |
| |
| // Mark globals and incomplete nodes as live... (this handles arguments) |
| if (F.getName() != "main") |
| for (unsigned i = 0, e = Nodes.size(); i != e; ++i) |
| if (Nodes[i]->NodeType & (DSNode::GlobalNode | DSNode::Incomplete) && |
| Nodes[i]->NodeType & (DSNode::HeapNode)) |
| Nodes[i]->markReachableNodes(MarkedNodes); |
| |
| // Marked the returned node as alive... |
| if (DSNode *RetNode = G.getRetNode().getNode()) |
| if (RetNode->NodeType & DSNode::HeapNode) |
| RetNode->markReachableNodes(MarkedNodes); |
| |
| if (MarkedNodes.empty()) // We don't need to clone the function if there |
| return 0; // are no incoming arguments to be added. |
| |
| // Figure out what the arguments are to be for the new version of the function |
| const FunctionType *OldFuncTy = F.getFunctionType(); |
| std::vector<const Type*> ArgTys; |
| ArgTys.reserve(OldFuncTy->getParamTypes().size() + MarkedNodes.size()); |
| |
| FI.ArgNodes.reserve(MarkedNodes.size()); |
| for (hash_set<DSNode*>::iterator I = MarkedNodes.begin(), |
| E = MarkedNodes.end(); I != E; ++I) |
| if ((*I)->NodeType & DSNode::Incomplete) { |
| ArgTys.push_back(PoolDescPtr); // Add the appropriate # of pool descs |
| FI.ArgNodes.push_back(*I); |
| } |
| if (FI.ArgNodes.empty()) return 0; // No nodes to be pool allocated! |
| |
| ArgTys.insert(ArgTys.end(), OldFuncTy->getParamTypes().begin(), |
| OldFuncTy->getParamTypes().end()); |
| |
| |
| // Create the new function prototype |
| FunctionType *FuncTy = FunctionType::get(OldFuncTy->getReturnType(), ArgTys, |
| OldFuncTy->isVarArg()); |
| // Create the new function... |
| Function *New = new Function(FuncTy, true, F.getName(), F.getParent()); |
| |
| // Set the rest of the new arguments names to be PDa<n> and add entries to the |
| // pool descriptors map |
| std::map<DSNode*, Value*> &PoolDescriptors = FI.PoolDescriptors; |
| Function::aiterator NI = New->abegin(); |
| for (unsigned i = 0, e = FI.ArgNodes.size(); i != e; ++i, ++NI) { |
| NI->setName("PDa"); // Add pd entry |
| PoolDescriptors.insert(std::make_pair(FI.ArgNodes[i], NI)); |
| } |
| |
| // Map the existing arguments of the old function to the corresponding |
| // arguments of the new function. |
| std::map<const Value*, Value*> ValueMap; |
| for (Function::aiterator I = F.abegin(), E = F.aend(); I != E; ++I, ++NI) { |
| ValueMap[I] = NI; |
| NI->setName(I->getName()); |
| } |
| |
| // Populate the value map with all of the globals in the program. |
| // FIXME: This should be unneccesary! |
| Module &M = *F.getParent(); |
| for (Module::iterator I = M.begin(), E=M.end(); I!=E; ++I) ValueMap[I] = I; |
| for (Module::giterator I = M.gbegin(), E=M.gend(); I!=E; ++I) ValueMap[I] = I; |
| |
| // Perform the cloning. |
| std::vector<ReturnInst*> Returns; |
| CloneFunctionInto(New, &F, ValueMap, Returns); |
| |
| // Invert the ValueMap into the NewToOldValueMap |
| std::map<Value*, const Value*> &NewToOldValueMap = FI.NewToOldValueMap; |
| for (std::map<const Value*, Value*>::iterator I = ValueMap.begin(), |
| E = ValueMap.end(); I != E; ++I) |
| NewToOldValueMap.insert(std::make_pair(I->second, I->first)); |
| |
| return FI.Clone = New; |
| } |
| |
| |
| // processFunction - Pool allocate any data structures which are contained in |
| // the specified function... |
| // |
| void PoolAllocate::ProcessFunctionBody(Function &F, Function &NewF) { |
| DSGraph &G = BU->getDSGraph(F); |
| std::vector<DSNode*> &Nodes = G.getNodes(); |
| if (Nodes.empty()) return; // Quick exit if nothing to do... |
| |
| FuncInfo &FI = FunctionInfo[&F]; // Get FuncInfo for F |
| hash_set<DSNode*> &MarkedNodes = FI.MarkedNodes; |
| |
| DEBUG(std::cerr << "[" << F.getName() << "] Pool Allocate: "); |
| |
| // Loop over all of the nodes which are non-escaping, adding pool-allocatable |
| // ones to the NodesToPA vector. |
| std::vector<DSNode*> NodesToPA; |
| for (unsigned i = 0, e = Nodes.size(); i != e; ++i) |
| if (Nodes[i]->NodeType & DSNode::HeapNode && // Pick nodes with heap elems |
| !(Nodes[i]->NodeType & DSNode::Array) && // Doesn't handle arrays yet. |
| !MarkedNodes.count(Nodes[i])) // Can't be marked |
| NodesToPA.push_back(Nodes[i]); |
| |
| DEBUG(std::cerr << NodesToPA.size() << " nodes to pool allocate\n"); |
| if (!NodesToPA.empty()) { |
| // Create pool construction/destruction code |
| std::map<DSNode*, Value*> &PoolDescriptors = FI.PoolDescriptors; |
| CreatePools(NewF, NodesToPA, PoolDescriptors); |
| } |
| |
| // Transform the body of the function now... |
| TransformFunctionBody(NewF, G, FI); |
| } |
| |
| |
| // CreatePools - This creates the pool initialization and destruction code for |
| // the DSNodes specified by the NodesToPA list. This adds an entry to the |
| // PoolDescriptors map for each DSNode. |
| // |
| void PoolAllocate::CreatePools(Function &F, |
| const std::vector<DSNode*> &NodesToPA, |
| std::map<DSNode*, Value*> &PoolDescriptors) { |
| // Find all of the return nodes in the CFG... |
| std::vector<BasicBlock*> ReturnNodes; |
| for (Function::iterator I = F.begin(), E = F.end(); I != E; ++I) |
| if (isa<ReturnInst>(I->getTerminator())) |
| ReturnNodes.push_back(I); |
| |
| TargetData &TD = getAnalysis<TargetData>(); |
| |
| // Loop over all of the pools, inserting code into the entry block of the |
| // function for the initialization and code in the exit blocks for |
| // destruction. |
| // |
| Instruction *InsertPoint = F.front().begin(); |
| for (unsigned i = 0, e = NodesToPA.size(); i != e; ++i) { |
| DSNode *Node = NodesToPA[i]; |
| |
| // Create a new alloca instruction for the pool... |
| Value *AI = new AllocaInst(PoolDescType, 0, "PD", InsertPoint); |
| |
| Value *ElSize = |
| ConstantUInt::get(Type::UIntTy, TD.getTypeSize(Node->getType())); |
| |
| // Insert the call to initialize the pool... |
| new CallInst(PoolInit, make_vector(AI, ElSize, 0), "", InsertPoint); |
| |
| // Update the PoolDescriptors map |
| PoolDescriptors.insert(std::make_pair(Node, AI)); |
| |
| // Insert a call to pool destroy before each return inst in the function |
| for (unsigned r = 0, e = ReturnNodes.size(); r != e; ++r) |
| new CallInst(PoolDestroy, make_vector(AI, 0), "", |
| ReturnNodes[r]->getTerminator()); |
| } |
| } |
| |
| |
| namespace { |
| /// FuncTransform - This class implements transformation required of pool |
| /// allocated functions. |
| struct FuncTransform : public InstVisitor<FuncTransform> { |
| PoolAllocate &PAInfo; |
| DSGraph &G; |
| FuncInfo &FI; |
| |
| FuncTransform(PoolAllocate &P, DSGraph &g, FuncInfo &fi) |
| : PAInfo(P), G(g), FI(fi) {} |
| |
| void visitMallocInst(MallocInst &MI); |
| void visitFreeInst(FreeInst &FI); |
| void visitCallInst(CallInst &CI); |
| |
| private: |
| DSNode *getDSNodeFor(Value *V) { |
| if (!FI.NewToOldValueMap.empty()) { |
| // If the NewToOldValueMap is in effect, use it. |
| std::map<Value*,const Value*>::iterator I = FI.NewToOldValueMap.find(V); |
| if (I != FI.NewToOldValueMap.end()) |
| V = (Value*)I->second; |
| } |
| |
| return G.getScalarMap()[V].getNode(); |
| } |
| Value *getPoolHandle(Value *V) { |
| DSNode *Node = getDSNodeFor(V); |
| // Get the pool handle for this DSNode... |
| std::map<DSNode*, Value*>::iterator I = FI.PoolDescriptors.find(Node); |
| return I != FI.PoolDescriptors.end() ? I->second : 0; |
| } |
| }; |
| } |
| |
| void PoolAllocate::TransformFunctionBody(Function &F, DSGraph &G, FuncInfo &FI){ |
| FuncTransform(*this, G, FI).visit(F); |
| } |
| |
| |
| void FuncTransform::visitMallocInst(MallocInst &MI) { |
| // Get the pool handle for the node that this contributes to... |
| Value *PH = getPoolHandle(&MI); |
| if (PH == 0) return; |
| |
| // Insert a call to poolalloc |
| Value *V = new CallInst(PAInfo.PoolAlloc, make_vector(PH, 0), |
| MI.getName(), &MI); |
| MI.setName(""); // Nuke MIs name |
| |
| // Cast to the appropriate type... |
| Value *Casted = new CastInst(V, MI.getType(), V->getName(), &MI); |
| |
| // Update def-use info |
| MI.replaceAllUsesWith(Casted); |
| |
| // Remove old malloc instruction |
| MI.getParent()->getInstList().erase(&MI); |
| |
| hash_map<Value*, DSNodeHandle> &SM = G.getScalarMap(); |
| hash_map<Value*, DSNodeHandle>::iterator MII = SM.find(&MI); |
| |
| // If we are modifying the original function, update the DSGraph... |
| if (MII != SM.end()) { |
| // V and Casted now point to whatever the original malloc did... |
| SM.insert(std::make_pair(V, MII->second)); |
| SM.insert(std::make_pair(Casted, MII->second)); |
| SM.erase(MII); // The malloc is now destroyed |
| } else { // Otherwise, update the NewToOldValueMap |
| std::map<Value*,const Value*>::iterator MII = |
| FI.NewToOldValueMap.find(&MI); |
| assert(MII != FI.NewToOldValueMap.end() && "MI not found in clone?"); |
| FI.NewToOldValueMap.insert(std::make_pair(V, MII->second)); |
| FI.NewToOldValueMap.insert(std::make_pair(Casted, MII->second)); |
| FI.NewToOldValueMap.erase(MII); |
| } |
| } |
| |
| void FuncTransform::visitFreeInst(FreeInst &FI) { |
| Value *Arg = FI.getOperand(0); |
| Value *PH = getPoolHandle(Arg); // Get the pool handle for this DSNode... |
| if (PH == 0) return; |
| // Insert a cast and a call to poolfree... |
| Value *Casted = new CastInst(Arg, PointerType::get(Type::SByteTy), |
| Arg->getName()+".casted", &FI); |
| new CallInst(PAInfo.PoolFree, make_vector(PH, Casted, 0), "", &FI); |
| |
| // Delete the now obsolete free instruction... |
| FI.getParent()->getInstList().erase(&FI); |
| } |
| |
| static void CalcNodeMapping(DSNode *Caller, DSNode *Callee, |
| std::map<DSNode*, DSNode*> &NodeMapping) { |
| if (Callee == 0) return; |
| assert(Caller && "Callee has node but caller doesn't??"); |
| |
| std::map<DSNode*, DSNode*>::iterator I = NodeMapping.find(Callee); |
| if (I != NodeMapping.end()) { // Node already in map... |
| assert(I->second == Caller && "Node maps to different nodes on paths?"); |
| } else { |
| NodeMapping.insert(I, std::make_pair(Callee, Caller)); |
| |
| // Recursively add pointed to nodes... |
| for (unsigned i = 0, e = Callee->getNumLinks(); i != e; ++i) |
| CalcNodeMapping(Caller->getLink(i << DS::PointerShift).getNode(), |
| Callee->getLink(i << DS::PointerShift).getNode(), |
| NodeMapping); |
| } |
| } |
| |
| void FuncTransform::visitCallInst(CallInst &CI) { |
| Function *CF = CI.getCalledFunction(); |
| assert(CF && "FIXME: Pool allocation doesn't handle indirect calls!"); |
| |
| FuncInfo *CFI = PAInfo.getFuncInfo(*CF); |
| if (CFI == 0 || CFI->Clone == 0) return; // Nothing to transform... |
| |
| DEBUG(std::cerr << " Handling call: " << CI); |
| |
| DSGraph &CG = PAInfo.getBUDataStructures().getDSGraph(*CF); // Callee graph |
| |
| // We need to figure out which local pool descriptors correspond to the pool |
| // descriptor arguments passed into the function call. Calculate a mapping |
| // from callee DSNodes to caller DSNodes. We construct a partial isomophism |
| // between the graphs to figure out which pool descriptors need to be passed |
| // in. The roots of this mapping is found from arguments and return values. |
| // |
| std::map<DSNode*, DSNode*> NodeMapping; |
| |
| Function::aiterator AI = CF->abegin(), AE = CF->aend(); |
| unsigned OpNum = 1; |
| for (; AI != AE; ++AI, ++OpNum) |
| CalcNodeMapping(getDSNodeFor(CI.getOperand(OpNum)), |
| CG.getScalarMap()[AI].getNode(), NodeMapping); |
| assert(OpNum == CI.getNumOperands() && "Varargs calls not handled yet!"); |
| |
| // Map the return value as well... |
| CalcNodeMapping(getDSNodeFor(&CI), CG.getRetNode().getNode(), NodeMapping); |
| |
| |
| // Okay, now that we have established our mapping, we can figure out which |
| // pool descriptors to pass in... |
| std::vector<Value*> Args; |
| |
| // Add an argument for each pool which must be passed in... |
| for (unsigned i = 0, e = CFI->ArgNodes.size(); i != e; ++i) { |
| if (NodeMapping.count(CFI->ArgNodes[i])) { |
| assert(NodeMapping.count(CFI->ArgNodes[i]) && "Node not in mapping!"); |
| DSNode *LocalNode = NodeMapping.find(CFI->ArgNodes[i])->second; |
| assert(FI.PoolDescriptors.count(LocalNode) && "Node not pool allocated?"); |
| Args.push_back(FI.PoolDescriptors.find(LocalNode)->second); |
| } else { |
| Args.push_back(Constant::getNullValue(PoolDescPtr)); |
| } |
| } |
| |
| // Add the rest of the arguments... |
| Args.insert(Args.end(), CI.op_begin()+1, CI.op_end()); |
| |
| std::string Name = CI.getName(); CI.setName(""); |
| Value *NewCall = new CallInst(CFI->Clone, Args, Name, &CI); |
| CI.replaceAllUsesWith(NewCall); |
| |
| DEBUG(std::cerr << " Result Call: " << *NewCall); |
| CI.getParent()->getInstList().erase(&CI); |
| } |