blob: 45b16124971df40c9166b438adb5d93d950ed4e6 [file] [log] [blame]
//===-- TransformFunctionBody.cpp - Pool Function Transformer -------------===//
//
// This file defines the PoolAllocate::TransformBody method.
//
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "PoolAllocator"
#include "poolalloc/PoolAllocate.h"
#include "llvm/Analysis/DataStructure.h"
#include "llvm/Analysis/DSGraph.h"
#include "llvm/Function.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/Debug.h"
#include "Support/VectorExtras.h"
using namespace llvm;
using namespace PA;
namespace {
/// FuncTransform - This class implements transformation required of pool
/// allocated functions.
struct FuncTransform : public InstVisitor<FuncTransform> {
PoolAllocate &PAInfo;
DSGraph &G; // The Bottom-up DS Graph
FuncInfo &FI;
// PoolUses - For each pool (identified by the pool descriptor) keep track
// of which blocks require the memory in the pool to not be freed. This
// does not include poolfree's. Note that this is only tracked for pools
// which this is the home of, ie, they are Alloca instructions.
std::set<std::pair<AllocaInst*, Instruction*> > &PoolUses;
// PoolDestroys - For each pool, keep track of the actual poolfree calls
// inserted into the code. This is seperated out from PoolUses.
std::set<std::pair<AllocaInst*, CallInst*> > &PoolFrees;
FuncTransform(PoolAllocate &P, DSGraph &g, FuncInfo &fi,
std::set<std::pair<AllocaInst*, Instruction*> > &poolUses,
std::set<std::pair<AllocaInst*, CallInst*> > &poolFrees)
: PAInfo(P), G(g), FI(fi),
PoolUses(poolUses), PoolFrees(poolFrees) {
}
template <typename InstType, typename SetType>
void AddPoolUse(InstType &I, Value *PoolHandle, SetType &Set) {
if (AllocaInst *AI = dyn_cast<AllocaInst>(PoolHandle))
Set.insert(std::make_pair(AI, &I));
}
void visitInstruction(Instruction &I);
void visitMallocInst(MallocInst &MI);
void visitFreeInst(FreeInst &FI);
void visitCallSite(CallSite CS);
void visitCallInst(CallInst &CI) { visitCallSite(&CI); }
void visitInvokeInst(InvokeInst &II) { visitCallSite(&II); }
void visitLoadInst(LoadInst &I);
void visitStoreInst (StoreInst &I);
private:
DSNodeHandle& getDSNodeHFor(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];
}
Value *getPoolHandle(Value *V) {
DSNode *Node = getDSNodeHFor(V).getNode();
// 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;
}
Function* retCloneIfFunc(Value *V);
};
}
void PoolAllocate::TransformBody(DSGraph &g, PA::FuncInfo &fi,
std::set<std::pair<AllocaInst*,Instruction*> > &poolUses,
std::set<std::pair<AllocaInst*, CallInst*> > &poolFrees,
Function &F) {
FuncTransform(*this, g, fi, poolUses, poolFrees).visit(F);
}
// Returns the clone if V is a static function (not a pointer) and belongs
// to an equivalence class i.e. is pool allocated
Function* FuncTransform::retCloneIfFunc(Value *V) {
if (ConstantPointerRef *CPR = dyn_cast<ConstantPointerRef>(V))
V = CPR->getValue();
if (Function *F = dyn_cast<Function>(V))
if (FuncInfo *FI = PAInfo.getFuncInfo(*F))
return FI->Clone;
return 0;
}
void FuncTransform::visitLoadInst(LoadInst &LI) {
if (Value *PH = getPoolHandle(LI.getOperand(0)))
AddPoolUse(LI, PH, PoolUses);
visitInstruction(LI);
}
void FuncTransform::visitStoreInst(StoreInst &SI) {
if (Value *PH = getPoolHandle(SI.getOperand(1)))
AddPoolUse(SI, PH, PoolUses);
visitInstruction(SI);
}
void FuncTransform::visitMallocInst(MallocInst &MI) {
// Get the pool handle for the node that this contributes to...
Value *PH = getPoolHandle(&MI);
// NB: PH is zero even if the node is collapsed
if (PH == 0) return;
std::string Name = MI.getName(); MI.setName("");
// Insert a call to poolalloc
TargetData &TD = PAInfo.getAnalysis<TargetData>();
Value *AllocSize =
ConstantUInt::get(Type::UIntTy, TD.getTypeSize(MI.getAllocatedType()));
if (MI.isArrayAllocation())
AllocSize = BinaryOperator::create(Instruction::Mul, AllocSize,
MI.getOperand(0), "sizetmp", &MI);
Instruction *V = new CallInst(PAInfo.PoolAlloc, make_vector(PH, AllocSize, 0),
Name, &MI);
AddPoolUse(*V, PH, PoolUses);
// Cast to the appropriate type if necessary
Value *Casted = V;
if (V->getType() != MI.getType())
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);
DSGraph::ScalarMapTy &SM = G.getScalarMap();
DSGraph::ScalarMapTy::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));
if (V != Casted)
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));
if (V != Casted)
FI.NewToOldValueMap.insert(std::make_pair(Casted, MII->second));
FI.NewToOldValueMap.erase(MII);
}
}
void FuncTransform::visitFreeInst(FreeInst &FrI) {
Value *Arg = FrI.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 = Arg;
if (Arg->getType() != PointerType::get(Type::SByteTy))
Casted = new CastInst(Arg, PointerType::get(Type::SByteTy),
Arg->getName()+".casted", &FrI);
CallInst *FreeI = new CallInst(PAInfo.PoolFree, make_vector(PH, Casted, 0),
"", &FrI);
// Delete the now obsolete free instruction...
FrI.getParent()->getInstList().erase(&FrI);
AddPoolUse(*FreeI, PH, PoolFrees);
// Update the NewToOldValueMap if this is a clone
if (!FI.NewToOldValueMap.empty()) {
std::map<Value*,const Value*>::iterator II =
FI.NewToOldValueMap.find(&FrI);
assert(II != FI.NewToOldValueMap.end() &&
"FrI not found in clone?");
FI.NewToOldValueMap.insert(std::make_pair(FreeI, II->second));
FI.NewToOldValueMap.erase(II);
}
}
void FuncTransform::visitCallSite(CallSite CS) {
Function *CF = CS.getCalledFunction();
Instruction *TheCall = CS.getInstruction();
// optimization for function pointers that are basically gotten from a cast
// with only one use and constant expressions with casts in them
if (!CF) {
Value *CV = CS.getCalledValue();
if (CastInst* CastI = dyn_cast<CastInst>(CV)) {
if (isa<Function>(CastI->getOperand(0)) &&
CastI->getOperand(0)->getType() == CastI->getType())
CF = dyn_cast<Function>(CastI->getOperand(0));
} else if (ConstantExpr *CE = dyn_cast<ConstantExpr>(CV)) {
if (CE->getOpcode() == Instruction::Cast)
if (ConstantPointerRef *CPR
= dyn_cast<ConstantPointerRef>(CE->getOperand(0)))
CF = dyn_cast<Function>(CPR->getValue());
}
}
// 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.
//
DSGraph::NodeMapTy NodeMapping;
Instruction *NewCall;
Value *NewCallee;
std::vector<DSNode*> ArgNodes;
DSGraph *CalleeGraph; // The callee graph
if (CF) { // Direct calls are nice and simple.
FuncInfo *CFI = PAInfo.getFuncInfo(*CF);
if (CFI == 0 || CFI->Clone == 0) { // Nothing to transform...
visitInstruction(*TheCall);
return;
}
NewCallee = CFI->Clone;
ArgNodes = CFI->ArgNodes;
DEBUG(std::cerr << " Handling direct call: " << *TheCall);
CalleeGraph = &PAInfo.getBUDataStructures().getDSGraph(*CF);
} else {
DEBUG(std::cerr << " Handling indirect call: " << *TheCall);
// Figure out which set of functions this call may invoke
Instruction *OrigInst = CS.getInstruction();
// If this call site is in a clone function, map it back to the original
if (!FI.NewToOldValueMap.empty())
OrigInst = cast<Instruction>((Value*)FI.NewToOldValueMap[OrigInst]);
const PA::EquivClassInfo &ECI =
PAInfo.getECIForIndirectCallSite(CallSite::get(OrigInst));
if (ECI.ArgNodes.empty())
return; // No arguments to add? Transformation is a noop!
// Here we fill in CF with one of the possible called functions. Because we
// merged together all of the arguments to all of the functions in the
// equivalence set, it doesn't really matter which one we pick.
CalleeGraph = ECI.G;
CF = ECI.FuncsInClass.back();
NewCallee = CS.getCalledValue();
ArgNodes = ECI.ArgNodes;
// Cast the function pointer to an appropriate type!
std::vector<const Type*> ArgTys(ArgNodes.size(),
PoolAllocate::PoolDescPtrTy);
for (CallSite::arg_iterator I = CS.arg_begin(), E = CS.arg_end();
I != E; ++I)
ArgTys.push_back((*I)->getType());
FunctionType *FTy = FunctionType::get(TheCall->getType(), ArgTys, false);
PointerType *PFTy = PointerType::get(FTy);
// If there are any pool arguments cast the function pointer to the right
// type.
NewCallee = new CastInst(NewCallee, PFTy, "tmp", TheCall);
}
Function::aiterator FAI = CF->abegin(), E = CF->aend();
CallSite::arg_iterator AI = CS.arg_begin(), AE = CS.arg_end();
for ( ; FAI != E && AI != AE; ++FAI, ++AI)
if (!isa<Constant>(*AI))
DSGraph::computeNodeMapping(CalleeGraph->getNodeForValue(FAI),
getDSNodeHFor(*AI), NodeMapping, false);
assert(AI == AE && "Varargs calls not handled yet!");
// Map the return value as well...
if (TheCall->getType() != Type::VoidTy)
DSGraph::computeNodeMapping(CalleeGraph->getReturnNodeFor(*CF),
getDSNodeHFor(TheCall), NodeMapping, false);
#if 1
// Map the nodes that are pointed to by globals.
// For all globals map getDSNodeForGlobal(g)->CG.getDSNodeForGlobal(g)
for (DSGraph::ScalarMapTy::iterator SMI = G.getScalarMap().begin(),
SME = G.getScalarMap().end(); SMI != SME; ++SMI)
if (GlobalValue *GV = dyn_cast<GlobalValue>(SMI->first))
DSGraph::computeNodeMapping(CalleeGraph->getNodeForValue(GV),
SMI->second, NodeMapping, false);
#endif
// Okay, now that we have established our mapping, we can figure out which
// pool descriptors to pass in...
std::vector<Value*> Args;
for (unsigned i = 0, e = ArgNodes.size(); i != e; ++i) {
Value *ArgVal = 0;
if (NodeMapping.count(ArgNodes[i]))
if (DSNode *LocalNode = NodeMapping[ArgNodes[i]].getNode())
if (FI.PoolDescriptors.count(LocalNode))
ArgVal = FI.PoolDescriptors.find(LocalNode)->second;
Args.push_back(ArgVal ? ArgVal :
Constant::getNullValue(PoolAllocate::PoolDescPtrTy));
}
// Add the rest of the arguments...
Args.insert(Args.end(), CS.arg_begin(), CS.arg_end());
std::string Name = TheCall->getName(); TheCall->setName("");
if (InvokeInst *II = dyn_cast<InvokeInst>(TheCall)) {
NewCall = new InvokeInst(NewCallee, II->getNormalDest(),
II->getExceptionalDest(), Args, Name, TheCall);
} else {
NewCall = new CallInst(NewCallee, Args, Name, TheCall);
}
// Add all of the uses of the pool descriptor
for (unsigned i = 0, e = ArgNodes.size(); i != e; ++i)
AddPoolUse(*NewCall, Args[i], PoolUses);
TheCall->replaceAllUsesWith(NewCall);
DEBUG(std::cerr << " Result Call: " << *NewCall);
if (TheCall->getType() != Type::VoidTy) {
// If we are modifying the original function, update the DSGraph...
DSGraph::ScalarMapTy &SM = G.getScalarMap();
DSGraph::ScalarMapTy::iterator CII = SM.find(TheCall);
if (CII != SM.end()) {
SM.insert(std::make_pair(NewCall, CII->second));
SM.erase(CII); // Destroy the CallInst
} else {
// Otherwise update the NewToOldValueMap with the new CI return value
std::map<Value*,const Value*>::iterator CII =
FI.NewToOldValueMap.find(TheCall);
assert(CII != FI.NewToOldValueMap.end() && "CI not found in clone?");
FI.NewToOldValueMap.insert(std::make_pair(NewCall, CII->second));
FI.NewToOldValueMap.erase(CII);
}
} else if (!FI.NewToOldValueMap.empty()) {
std::map<Value*,const Value*>::iterator II =
FI.NewToOldValueMap.find(TheCall);
assert(II != FI.NewToOldValueMap.end() &&
"CI not found in clone?");
FI.NewToOldValueMap.insert(std::make_pair(NewCall, II->second));
FI.NewToOldValueMap.erase(II);
}
TheCall->getParent()->getInstList().erase(TheCall);
visitInstruction(*NewCall);
}
// visitInstruction - For all instructions in the transformed function bodies,
// replace any references to the original calls with references to the
// transformed calls. Many instructions can "take the address of" a function,
// and we must make sure to catch each of these uses, and transform it into a
// reference to the new, transformed, function.
void FuncTransform::visitInstruction(Instruction &I) {
for (unsigned i = 0, e = I.getNumOperands(); i != e; ++i)
if (Function *clonedFunc = retCloneIfFunc(I.getOperand(i))) {
Constant *CF = ConstantPointerRef::get(clonedFunc);
I.setOperand(i, ConstantExpr::getCast(CF, I.getOperand(i)->getType()));
}
}