From lattner at cs.uiuc.edu Mon Nov 8 00:18:17 2004
From: lattner at cs.uiuc.edu (Chris Lattner)
Date: Mon, 8 Nov 2004 00:18:17 -0600
Subject: [llvm-commits] CVS: poolalloc/lib/PoolAllocate/Heuristic.cpp
Heuristic.h PoolAllocate.cpp PoolAllocate.h
Message-ID: <200411080618.iA86IHq4018989@apoc.cs.uiuc.edu>
Changes in directory poolalloc/lib/PoolAllocate:
Heuristic.cpp added (r1.1)
Heuristic.h added (r1.1)
PoolAllocate.cpp updated: 1.86 -> 1.87
PoolAllocate.h updated: 1.28 -> 1.29
---
Log message:
Introduce a new *formal* interface between the pool allocator and the various
heuristics it supports. This gives us a uniform way to handle the global
pools and function local pools, and makes it easy to implement coallescing
heuristics. This also removes most of the special purpose hacks for the
various heuristics spread throughout the pool allocator.
The pool allocator now looks like this:
$ wc *.cpp *.h
439 1904 17119 EquivClassGraphs.cpp
398 1728 15760 Heuristic.cpp
817 3389 32438 PoolAllocate.cpp
529 2092 20645 TransformFunctionBody.cpp
106 447 3785 EquivClassGraphs.h
93 376 3010 Heuristic.h
206 936 7455 PoolAllocate.h
2588 10872 100212 total
---
Diffs of the changes: (+572 -332)
Index: poolalloc/lib/PoolAllocate/Heuristic.cpp
diff -c /dev/null poolalloc/lib/PoolAllocate/Heuristic.cpp:1.1
*** /dev/null Mon Nov 8 00:18:17 2004
--- poolalloc/lib/PoolAllocate/Heuristic.cpp Mon Nov 8 00:18:07 2004
***************
*** 0 ****
--- 1,398 ----
+ //===-- Heuristic.cpp - Interface to PA heuristics ------------------------===//
+ //
+ // 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 implements the various pool allocation heuristics.
+ //
+ //===----------------------------------------------------------------------===//
+
+ #include "Heuristic.h"
+ #include "PoolAllocate.h"
+ #include "llvm/Instructions.h"
+ #include "llvm/Module.h"
+ #include "llvm/Analysis/DataStructure/DSGraphTraits.h"
+ #include "llvm/ADT/DepthFirstIterator.h"
+ #include "llvm/Support/CommandLine.h"
+ #include "llvm/Target/TargetData.h"
+ using namespace llvm;
+ using namespace PA;
+
+ namespace {
+ enum PoolAllocHeuristic {
+ NoNodes,
+ OnlyOverhead,
+ AllInOneGlobalPool,
+ SmartCoallesceNodes,
+ CyclicNodes,
+ AllButUnreachableFromMemory,
+ AllNodes,
+ };
+ cl::opt
+ TheHeuristic("poolalloc-heuristic",
+ cl::desc("Heuristic to choose which nodes to pool allocate"),
+ cl::values(clEnumVal(AllNodes, " Pool allocate all nodes"),
+ clEnumVal(AllButUnreachableFromMemory, " Pool allocate all reachable from memory objects"),
+ clEnumVal(CyclicNodes, " Pool allocate nodes with cycles"),
+ clEnumVal(SmartCoallesceNodes, " Use the smart node merging heuristic"),
+ clEnumVal(AllInOneGlobalPool, " Use pool library as replacement for malloc/free"),
+ clEnumVal(OnlyOverhead, " Do not pool allocate anything, but induce all overhead from it"),
+ clEnumVal(NoNodes, " Do not pool allocate anything"),
+ clEnumValEnd),
+ cl::init(AllButUnreachableFromMemory));
+ }
+
+ Heuristic::~Heuristic() {}
+
+ unsigned Heuristic::getRecommendedSize(DSNode *N) {
+ unsigned PoolSize = 0;
+ if (!N->isArray() && N->getType()->isSized()) {
+ DSGraph *G = N->getParentGraph();
+ PoolSize = G->getTargetData().getTypeSize(N->getType());
+ }
+ if (PoolSize == 1) PoolSize = 0;
+ return PoolSize;
+ }
+
+ //===-- AllNodes Heuristic ------------------------------------------------===//
+ //
+ // This heuristic pool allocates everything possible into separate pools.
+ //
+ struct AllNodesHeuristic : public Heuristic {
+
+ void AssignToPools(const std::vector &NodesToPA,
+ Function *F, DSGraph &G,
+ std::vector &ResultPools) {
+ for (unsigned i = 0, e = NodesToPA.size(); i != e; ++i)
+ ResultPools.push_back(OnePool(NodesToPA[i]));
+ }
+ };
+
+
+ //===-- AllButUnreachableFromMemoryHeuristic Heuristic --------------------===//
+ //
+ //
+ // This heuristic pool allocates everything possible into separate pools, unless
+ // the pool is not reachable by other memory objects. This filters out objects
+ // that are not cyclic and are only pointed to by scalars: these tend to be
+ // singular memory allocations that are not worth creating a whole pool for.
+ //
+ struct AllButUnreachableFromMemoryHeuristic : public Heuristic {
+
+ void AssignToPools(const std::vector &NodesToPA,
+ Function *F, DSGraph &G,
+ std::vector &ResultPools) {
+ // FIXME: implement
+ for (unsigned i = 0, e = NodesToPA.size(); i != e; ++i)
+ ResultPools.push_back(OnePool(NodesToPA[i]));
+ }
+ };
+
+ //===-- CyclicNodes Heuristic ---------------------------------------------===//
+ //
+ // This heuristic only pool allocates nodes in an SCC in the DSGraph.
+ //
+ struct CyclicNodesHeuristic : public Heuristic {
+
+ void AssignToPools(const std::vector &NodesToPA,
+ Function *F, DSGraph &G,
+ std::vector &ResultPools);
+ };
+
+ static bool NodeExistsInCycle(DSNode *N) {
+ for (DSNode::iterator I = N->begin(), E = N->end(); I != E; ++I)
+ if (*I && std::find(df_begin(*I), df_end(*I), N) != df_end(*I))
+ return true;
+ return false;
+ }
+
+ void CyclicNodesHeuristic::AssignToPools(const std::vector &NodesToPA,
+ Function *F, DSGraph &G,
+ std::vector &ResultPools) {
+ for (unsigned i = 0, e = NodesToPA.size(); i != e; ++i)
+ if (NodeExistsInCycle(NodesToPA[i]))
+ ResultPools.push_back(OnePool(NodesToPA[i]));
+ }
+
+
+ //===-- SmartCoallesceNodes Heuristic -------------------------------------===//
+ //
+ // This heuristic attempts to be smart and coallesce nodes at times. In
+ // practice, it doesn't work very well.
+ //
+ struct SmartCoallesceNodesHeuristic : public Heuristic {
+
+ void AssignToPools(const std::vector &NodesToPA,
+ Function *F, DSGraph &G,
+ std::vector &ResultPools) {
+ // For globals, do not pool allocate unless the node is cyclic and not an
+ // array (unless it's collapsed).
+ if (F == 0) {
+ for (unsigned i = 0, e = NodesToPA.size(); i != e; ++i) {
+ DSNode *Node = NodesToPA[i];
+ if ((Node->isNodeCompletelyFolded() || !Node->isArray()) &&
+ NodeExistsInCycle(Node))
+ ResultPools.push_back(OnePool(Node));
+ }
+ } else {
+ // TODO
+ }
+ }
+ };
+
+ #if 0
+ // Heuristic for building per-function pools
+
+ switch (Heuristic) {
+ case SmartCoallesceNodes: {
+ std::set NodesToPASet(NodesToPA.begin(), NodesToPA.end());
+
+ // DSGraphs only have unidirectional edges, to traverse or inspect the
+ // predecessors of nodes, we must build a mapping of the inverse graph.
+ std::map > InverseGraph;
+
+ for (unsigned i = 0, e = NodesToPA.size(); i != e; ++i) {
+ DSNode *Node = NodesToPA[i];
+ for (DSNode::iterator CI = Node->begin(), E = Node->end(); CI != E; ++CI)
+ if (DSNode *Child = const_cast(*CI))
+ if (NodesToPASet.count(Child))
+ InverseGraph[Child].push_back(Node);
+ }
+
+ // Traverse the heap nodes in reverse-post-order so that we are guaranteed
+ // to visit all nodes pointing to another node before we visit that node
+ // itself (except with cycles).
+
+ // FIXME: This really should be using the PostOrderIterator.h file stuff,
+ // but the routines there do not support external storage!
+ std::set Visited;
+ std::vector Order;
+ for (unsigned i = 0, e = NodesToPA.size(); i != e; ++i)
+ POVisit(NodesToPA[i], Visited, Order);
+
+ // We want RPO, not PO, so reverse the order.
+ std::reverse(Order.begin(), Order.end());
+
+ // Okay, we have an ordering of the nodes in reverse post order. Traverse
+ // each node in this ordering, noting that there may be nodes in the order
+ // that are not in our NodesToPA list.
+ for (unsigned i = 0, e = Order.size(); i != e; ++i)
+ if (NodesToPASet.count(Order[i])) { // Only process pa nodes.
+ DSNode *N = Order[i];
+
+ // If this node has a backedge to itself, pool allocate it in a new
+ // pool.
+ if (NodeIsSelfRecursive(N)) {
+ // Create a new alloca instruction for the pool...
+ Value *AI = new AllocaInst(PoolDescType, 0, "PD", InsertPoint);
+
+ // Void types in DS graph are never used
+ if (N->isNodeCompletelyFolded())
+ std::cerr << "Node collapsing in '" << F.getName() << "'\n";
+
+ // Update the PoolDescriptors map
+ PoolDescriptors.insert(std::make_pair(N, AI));
+ #if 1
+ } else if (N->isArray() && !N->isNodeCompletelyFolded()) {
+ // We never pool allocate array nodes.
+ PoolDescriptors[N] =
+ Constant::getNullValue(PointerType::get(PoolDescType));
+ ++NumNonprofit;
+ #endif
+ } else {
+ // Otherwise the node is not self recursive. If the node is not an
+ // array, we can co-locate it with the pool of a predecessor node if
+ // any has been pool allocated, and start a new pool if a predecessor
+ // is an array. If there is a predecessor of this node that has not
+ // been visited yet in this RPO traversal, that means there is a
+ // cycle, so we choose to pool allocate this node right away.
+ //
+ // If there multiple predecessors in multiple different pools, we
+ // don't pool allocate this at all.
+
+ // Check out each of the predecessors of this node.
+ std::vector &Preds = InverseGraph[N];
+ Value *PredPool = 0;
+ bool HasUnvisitedPred = false;
+ bool HasArrayPred = false;
+ bool HasMultiplePredPools = false;
+ for (unsigned p = 0, e = Preds.size(); p != e; ++p) {
+ DSNode *Pred = Preds[p];
+ if (!PoolDescriptors.count(Pred))
+ HasUnvisitedPred = true; // no pool assigned to predecessor?
+ else if (Pred->isArray() && !Pred->isNodeCompletelyFolded())
+ HasArrayPred = true;
+ else if (PredPool && PoolDescriptors[Pred] != PredPool)
+ HasMultiplePredPools = true;
+ else if (!PredPool &&
+ !isa(PoolDescriptors[Pred]))
+ PredPool = PoolDescriptors[Pred];
+ // Otherwise, this predecessor has the same pool as a previous one.
+ }
+
+ if (HasMultiplePredPools) {
+ // If this node has predecessors that are in different pools, don't
+ // pool allocate this node.
+ PoolDescriptors[N] =
+ Constant::getNullValue(PointerType::get(PoolDescType));
+ ++NumNonprofit;
+ } else if (PredPool) {
+ // If all of the predecessors of this node are already in a pool,
+ // colocate.
+ PoolDescriptors[N] = PredPool;
+ ++NumColocated;
+ } else if (HasArrayPred || HasUnvisitedPred) {
+ // If this node has an array predecessor, or if there is a
+ // predecessor that has not been visited yet, allocate a new pool
+ // for it.
+ Value *AI = new AllocaInst(PoolDescType, 0, "PD", InsertPoint);
+ if (N->isNodeCompletelyFolded())
+ std::cerr << "Node collapsing in '" << F.getName() << "'\n";
+
+ PoolDescriptors[N] = AI;
+ } else {
+ // If this node has no pool allocated predecessors, and there is no
+ // reason to pool allocate it, don't.
+ assert(PredPool == 0);
+ PoolDescriptors[N] =
+ Constant::getNullValue(PointerType::get(PoolDescType));
+ ++NumNonprofit;
+ }
+ }
+ }
+ } // End switch case
+ } // end switch
+ #endif
+
+
+ //===-- AllInOneGlobalPool Heuristic --------------------------------------===//
+ //
+ // This heuristic puts all memory in the whole program into a single global
+ // pool. This is not safe, and is not good for performance, but can be used to
+ // evaluate how good the pool allocator runtime works as a "malloc replacement".
+ //
+ struct AllInOneGlobalPoolHeuristic : public Heuristic {
+ // TheGlobalPD - This global pool is the one and only one used when running
+ // with Heuristic=AllInOneGlobalPool.
+ GlobalVariable *TheGlobalPD;
+
+ AllInOneGlobalPoolHeuristic() : TheGlobalPD(0) {}
+
+
+ virtual bool IsRealHeuristic() { return false; }
+
+ void AssignToPools(const std::vector &NodesToPA,
+ Function *F, DSGraph &G,
+ std::vector &ResultPools) {
+ if (TheGlobalPD == 0)
+ TheGlobalPD = PA->CreateGlobalPool(0);
+
+ // All nodes allocate from the same global pool.
+ OnePool Pool;
+ Pool.NodesInPool = NodesToPA;
+ Pool.PoolDesc = TheGlobalPD;
+ ResultPools.push_back(Pool);
+ }
+ };
+
+ //===-- OnlyOverhead Heuristic --------------------------------------------===//
+ //
+ // This heuristic is a hack to evaluate how much overhead pool allocation adds
+ // to a program. It adds all of the arguments, poolinits and pool destroys to
+ // the program, but dynamically only passes null into the pool alloc/free
+ // functions, causing them to allocate from the heap.
+ //
+ struct OnlyOverheadHeuristic : public Heuristic {
+ virtual bool IsRealHeuristic() { return false; }
+
+ void AssignToPools(const std::vector &NodesToPA,
+ Function *F, DSGraph &G,
+ std::vector &ResultPools) {
+ // For this heuristic, we assign everything possible to its own pool.
+ for (unsigned i = 0, e = NodesToPA.size(); i != e; ++i)
+ ResultPools.push_back(OnePool(NodesToPA[i]));
+ }
+
+ void HackFunctionBody(Function &F, std::map &PDs);
+ };
+
+ /// getDynamicallyNullPool - Return a PoolDescriptor* that is always dynamically
+ /// null. Insert the code necessary to produce it before the specified
+ /// instruction.
+ static Value *getDynamicallyNullPool(BasicBlock::iterator I) {
+ // Arrange to dynamically pass null into all of the pool functions if we are
+ // only checking for overhead.
+ static Value *NullGlobal = 0;
+ if (!NullGlobal) {
+ Module *M = I->getParent()->getParent()->getParent();
+ NullGlobal = new GlobalVariable(PoolAllocate::PoolDescPtrTy, false,
+ GlobalValue::ExternalLinkage,
+ Constant::getNullValue(PoolAllocate::PoolDescPtrTy),
+ "llvm-poolalloc-null-init", M);
+ }
+ while (isa(I)) ++I;
+
+ return new LoadInst(NullGlobal, "nullpd", I);
+ }
+
+ // HackFunctionBody - This method is called on every transformed function body.
+ // Basically it replaces all uses of real pool descriptors with dynamically null
+ // values. However, it leaves pool init/destroy alone.
+ void OnlyOverheadHeuristic::HackFunctionBody(Function &F,
+ std::map &PDs) {
+ Function *PoolInit = PA->PoolInit;
+ Function *PoolDestroy = PA->PoolDestroy;
+
+ Value *NullPD = getDynamicallyNullPool(F.front().begin());
+ for (std::map::iterator PDI = PDs.begin(), E = PDs.end();
+ PDI != E; ++PDI) {
+ Value *OldPD = PDI->second;
+ std::vector OldPDUsers(OldPD->use_begin(), OldPD->use_end());
+ for (unsigned i = 0, e = OldPDUsers.size(); i != e; ++i) {
+ CallSite PDUser = CallSite::get(cast(OldPDUsers[i]));
+ if (PDUser.getCalledValue() != PoolInit &&
+ PDUser.getCalledValue() != PoolDestroy) {
+ assert(PDUser.getInstruction()->getParent()->getParent() == &F &&
+ "Not in cur fn??");
+ PDUser.getInstruction()->replaceUsesOfWith(OldPD, NullPD);
+ }
+ }
+ }
+ }
+
+
+ //===-- NoNodes Heuristic -------------------------------------------------===//
+ //
+ // This dummy heuristic chooses to not pool allocate anything.
+ //
+ struct NoNodesHeuristic : public Heuristic {
+ virtual bool IsRealHeuristic() { return false; }
+
+ void AssignToPools(const std::vector &NodesToPA,
+ Function *F, DSGraph &G,
+ std::vector &ResultPools) {
+ // Nothing to pool allocate here.
+ }
+ };
+
+ //===----------------------------------------------------------------------===//
+ // Heuristic dispatch support
+ //
+
+ PA::Heuristic *Heuristic::create() {
+ switch (TheHeuristic) {
+ default: assert(0 && "Unknown heuristic!");
+ case AllNodes: return new AllNodesHeuristic();
+ case AllButUnreachableFromMemory:
+ return new AllButUnreachableFromMemoryHeuristic();
+ case CyclicNodes: return new CyclicNodesHeuristic();
+ case SmartCoallesceNodes: return new SmartCoallesceNodesHeuristic();
+ case AllInOneGlobalPool: return new AllInOneGlobalPoolHeuristic();
+ case OnlyOverhead: return new OnlyOverheadHeuristic();
+ case NoNodes: return new NoNodesHeuristic();
+ }
+ }
Index: poolalloc/lib/PoolAllocate/Heuristic.h
diff -c /dev/null poolalloc/lib/PoolAllocate/Heuristic.h:1.1
*** /dev/null Mon Nov 8 00:18:17 2004
--- poolalloc/lib/PoolAllocate/Heuristic.h Mon Nov 8 00:18:07 2004
***************
*** 0 ****
--- 1,93 ----
+ //===-- Heuristic.h - Interface to PA heuristics ----------------*- C++ -*-===//
+ //
+ // 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 header is the abstract interface used by the pool allocator to access
+ // the various heuristics supported.
+ //
+ //===----------------------------------------------------------------------===//
+
+ #ifndef POOLALLOCATION_HEURISTIC_H
+ #define POOLALLOCATION_HEURISTIC_H
+
+ #include
+ #include
+There are two types of signatures for LLVM bytecode: uncompressed and
+compressed as shown in the table below.
| Type |
- Field Description |
+ Uncompressed |
+ Compressed |
| char |
Constant "l" (0x6C) |
+ Constant "l" (0x6C) |
| char |
Constant "l" (0x6C) |
+ Constant "l" (0x6C) |
| char |
Constant "v" (0x76) |
+ Constant "v" (0x76) |
| char |
Constant "m" (0x6D) |
+ Constant "c" (0x63) |
+
+
+ | char |
+ N/A |
+ '0'=null,'1'=gzip,'2'=bzip2 |
+In other words, the uncompressed signature is just the characters 'llvm'
+while the compressed signature is the characters 'llvc' followed by an ascii
+digit ('0', '1', or '2') that indicates the kind of compression used. A value of
+'0' indicates that null compression was used. This can happen when compression
+was requested on a platform that wasn't configured for gzip or bzip2. A value of
+'1' means that the rest of the file is compressed using the gzip algorithm and
+should be uncompressed before interpretation. A value of '2' means that the rest
+of the file is compressed using the bzip2 algorithm and should be uncompressed
+before interpretation. In all cases, the data resulting from uncompression
+should be interpreted as if it occurred immediately after the 'llvm'
+signature (i.e. the uncompressed data begins with the
+Module Block
+NOTE: As of LLVM 1.4, all bytecode files produced by the LLVM tools
+are compressed byte default. To disable compression, pass the
+--disable-compression option to the tool, if it supports it.
@@ -1865,7 +1892,7 @@
Reid Spencer and Chris Lattner
The LLVM Compiler Infrastructure
-Last modified: $Date: 2004/11/07 00:59:57 $
+Last modified: $Date: 2004/11/08 08:55:21 $