1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
|
//===-- 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);
}
|