[llvm-commits] CVS: poolalloc/lib/PoolAllocate/Heuristic.cpp Heuristic.h PoolAllocate.cpp PoolAllocate.h

Chris Lattner lattner at cs.uiuc.edu
Sun Nov 7 22:18:17 PST 2004



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<PoolAllocHeuristic>
+   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<DSNode*> &NodesToPA,
+                      Function *F, DSGraph &G,
+                      std::vector<OnePool> &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<DSNode*> &NodesToPA,
+                      Function *F, DSGraph &G,
+                      std::vector<OnePool> &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<DSNode*> &NodesToPA,
+                      Function *F, DSGraph &G,
+                      std::vector<OnePool> &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<DSNode*> &NodesToPA,
+                                          Function *F, DSGraph &G,
+                                          std::vector<OnePool> &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<DSNode*> &NodesToPA,
+                     Function *F, DSGraph &G,
+                     std::vector<OnePool> &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<DSNode*> 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<DSNode*, std::vector<DSNode*> > 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<DSNode*>(*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<DSNode*> Visited;
+     std::vector<DSNode*> 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<DSNode*> &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<ConstantPointerNull>(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<DSNode*> &NodesToPA,
+                     Function *F, DSGraph &G,
+                     std::vector<OnePool> &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<DSNode*> &NodesToPA,
+                     Function *F, DSGraph &G,
+                     std::vector<OnePool> &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<DSNode*, Value*> &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<AllocaInst>(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<DSNode*, Value*> &PDs) {
+   Function *PoolInit = PA->PoolInit;
+   Function *PoolDestroy = PA->PoolDestroy;
+ 
+   Value *NullPD = getDynamicallyNullPool(F.front().begin());
+   for (std::map<DSNode*, Value*>::iterator PDI = PDs.begin(), E = PDs.end();
+        PDI != E; ++PDI) {
+     Value *OldPD = PDI->second;
+     std::vector<User*> OldPDUsers(OldPD->use_begin(), OldPD->use_end());
+     for (unsigned i = 0, e = OldPDUsers.size(); i != e; ++i) {
+       CallSite PDUser = CallSite::get(cast<Instruction>(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<DSNode*> &NodesToPA,
+                     Function *F, DSGraph &G,
+                     std::vector<OnePool> &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 <vector>
+ #include <map>
+ 
+ namespace llvm {
+   class Value;
+   class Function;
+   class Module;
+   class DSGraph;
+   class DSNode;
+   class PoolAllocate;
+ namespace PA {
+   class Heuristic {
+   protected:
+     Module *M;
+     DSGraph *GG;
+     PoolAllocate *PA;
+ 
+     Heuristic() {}
+   public:
+     void Initialize(Module &m, DSGraph &gg, PoolAllocate &pa) {
+       M = &m; GG = ≫ PA = &pa;
+     }
+     virtual ~Heuristic();
+ 
+     /// IsRealHeuristic - Return true if this is not a real pool allocation
+     /// heuristic.
+     virtual bool IsRealHeuristic() { return true; }
+ 
+     /// OnePool - This represents some number of nodes which are coallesced into
+     /// a pool.
+     struct OnePool {
+       // NodesInPool - The DS nodes to be allocated to this pool.  There may be
+       // multiple here if they are being coallesced into the same pool.
+       std::vector<DSNode*> NodesInPool;
+ 
+       // PoolDesc - If the heuristic wants the nodes allocated to a specific
+       // pool descriptor, it can specify it here, otherwise a new pool is
+       // created.
+       Value *PoolDesc;
+ 
+       // PoolSize - If the pool is to be created, indicate the "recommended
+       // size" for the pool here.  This gets passed into poolinit.
+       unsigned PoolSize;
+ 
+       OnePool() : PoolDesc(0), PoolSize(0) {}
+ 
+       OnePool(DSNode *N) : PoolDesc(0), PoolSize(getRecommendedSize(N)) {
+         NodesInPool.push_back(N);
+       }
+       OnePool(DSNode *N, Value *PD) : PoolDesc(PD), PoolSize(0) {
+         NodesInPool.push_back(N);
+       }
+     };
+ 
+     /// AssignToPools - Partition NodesToPA into a set of disjoint pools,
+     /// returning the result in ResultPools.  If this is a function being pool
+     /// allocated, F will not be null.
+     virtual void AssignToPools(const std::vector<DSNode*> &NodesToPA,
+                                Function *F, DSGraph &G,
+                                std::vector<OnePool> &ResultPools) = 0;
+ 
+     // Hacks for the OnlyOverhead heuristic.
+     virtual void HackFunctionBody(Function &F, std::map<DSNode*, Value*> &PDs){}
+ 
+     /// getRecommendedSize - Return the recommended pool size for this DSNode.
+     ///
+     static unsigned getRecommendedSize(DSNode *N);
+ 
+     /// create - This static ctor creates the heuristic, based on the command
+     /// line argument to choose the heuristic.
+     static Heuristic *create();
+   };
+ }
+ }
+ 
+ #endif


Index: poolalloc/lib/PoolAllocate/PoolAllocate.cpp
diff -u poolalloc/lib/PoolAllocate/PoolAllocate.cpp:1.86 poolalloc/lib/PoolAllocate/PoolAllocate.cpp:1.87
--- poolalloc/lib/PoolAllocate/PoolAllocate.cpp:1.86	Sun Nov  7 16:19:59 2004
+++ poolalloc/lib/PoolAllocate/PoolAllocate.cpp	Mon Nov  8 00:18:07 2004
@@ -13,8 +13,9 @@
 //===----------------------------------------------------------------------===//
 
 #define DEBUG_TYPE "poolalloc"
-#include "EquivClassGraphs.h"
 #include "PoolAllocate.h"
+#include "EquivClassGraphs.h"
+#include "Heuristic.h"
 #include "llvm/Constants.h"
 #include "llvm/DerivedTypes.h"
 #include "llvm/Instructions.h"
@@ -22,7 +23,6 @@
 #include "llvm/Constants.h"
 #include "llvm/Analysis/DataStructure/DataStructure.h"
 #include "llvm/Analysis/DataStructure/DSGraph.h"
-#include "llvm/Analysis/DataStructure/DSGraphTraits.h"
 #include "llvm/Support/CFG.h"
 #include "llvm/Target/TargetData.h"
 #include "llvm/Transforms/Utils/BasicBlockUtils.h"
@@ -62,37 +62,12 @@
   //                        poolfree?)
   const Type *PoolDescType;
 
-  enum PoolAllocHeuristic {
-    NoNodes,
-    OnlyOverhead,
-    AllInOneGlobalPool,
-    SmartCoallesceNodes,
-    CyclicNodes,
-    AllNodes,
-  };
-  cl::opt<PoolAllocHeuristic>
-  Heuristic("poolalloc-heuristic",
-            cl::desc("Heuristic to choose which nodes to pool allocate"),
-            cl::values(clEnumVal(AllNodes, "  Pool allocate all nodes"),
-                       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(AllNodes));
-  
   cl::opt<bool>
   DisableInitDestroyOpt("poolalloc-force-simple-pool-init",
                         cl::desc("Always insert poolinit/pooldestroy calls at start and exit of functions"), cl::init(true));
   cl::opt<bool>
   DisablePoolFreeOpt("poolalloc-force-all-poolfrees",
                      cl::desc("Do not try to elide poolfree's where possible"));
-
-
-  // TheGlobalPD - This global pool is the one and only one used when running
-  // with Heuristic=AllInOneGlobalPool.
-  GlobalVariable *TheGlobalPD = 0;
 }
 
 void PoolAllocate::getAnalysisUsage(AnalysisUsage &AU) const {
@@ -105,6 +80,9 @@
   CurModule = &M;
   ECGraphs = &getAnalysis<EquivClassGraphs>();   // folded inlined CBU graphs
 
+  CurHeuristic = Heuristic::create();
+  CurHeuristic->Initialize(M, ECGraphs->getGlobalsGraph(), *this);
+
   // Add the pool* prototypes to the module
   AddPoolPrototypes();
 
@@ -147,9 +125,10 @@
     F->replaceAllUsesWith(ConstantExpr::getCast(I->second, F->getType()));
   }
 
-  if (Heuristic != NoNodes && Heuristic != OnlyOverhead &&
-      Heuristic != AllInOneGlobalPool)
+  if (CurHeuristic->IsRealHeuristic())
     MicroOptimizePoolCalls();
+
+  delete CurHeuristic;
   return true;
 }
 
@@ -253,15 +232,6 @@
     G.getNodeForValue(*I).getNode()->markReachableNodes(NodesFromGlobals);
 }
 
-static void printNTOMap(std::map<Value*, const Value*> &NTOM) {
-  std::cerr << "NTOM MAP\n";
-  for (std::map<Value*, const Value *>::iterator I = NTOM.begin(), 
-	 E = NTOM.end(); I != E; ++I) {
-    if (!isa<Function>(I->first) && !isa<BasicBlock>(I->first))
-      std::cerr << *I->first << " to " << *I->second << "\n";
-  }
-}
-
 static void MarkNodesWhichMustBePassedIn(hash_set<DSNode*> &MarkedNodes,
                                          Function &F, DSGraph &G) {
   // Mark globals and incomplete nodes as live... (this handles arguments)
@@ -383,51 +353,6 @@
   return FI.Clone = New;
 }
 
-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;
-}
-
-/// NodeIsSelfRecursive - Return true if this node contains a pointer to itself.
-static bool NodeIsSelfRecursive(DSNode *N) {
-  for (DSNode::iterator I = N->begin(), E = N->end(); I != E; ++I)
-    if (*I == N) return true;
-  return false;
-}
-
-static bool ShouldPoolAllocate(DSNode *N) {
-  // Now that we have a list of all of the nodes that CAN be pool allocated, use
-  // a heuristic to filter out nodes which are not profitable to pool allocate.
-  switch (Heuristic) {
-  default:
-  case AllInOneGlobalPool:
-  case OnlyOverhead:
-  case AllNodes: return true;
-  case NoNodes: return false;
-  case CyclicNodes:  // Pool allocate nodes that are cyclic and not arrays
-    return NodeExistsInCycle(N);
-  }
-}
-
-/// POVisit - This implements functionality found in Support/PostOrderIterator.h
-/// but in a way that allows multiple roots to be used.  If PostOrderIterator
-/// supported an external set like DepthFirstIterator did I could eliminate this
-/// cruft.
-///
-static void POVisit(DSNode *N, std::set<DSNode*> &Visited,
-                    std::vector<DSNode*> &Order) {
-  if (!Visited.insert(N).second) return;  // already visited
-
-  // Visit all children before visiting this node.
-  for (DSNode::iterator I = N->begin(), E = N->end(); I != E; ++I)
-    if (DSNode *C = const_cast<DSNode*>(*I))
-      POVisit(C, Visited, Order);
-  // Now that we visited all of our children, add ourself to the order.
-  Order.push_back(N);
-}
-
 // SetupGlobalPools - Create global pools for all DSNodes in the globals graph
 // which contain heap objects.  If a global variable points to a piece of memory
 // allocated from the heap, this pool gets a global lifetime.  This is
@@ -455,10 +380,6 @@
       GlobalHeapNodes.erase(Last);
   }
   
-  // If we don't need to create any global pools, exit now.
-  if (GlobalHeapNodes.empty() &&
-      Heuristic != AllInOneGlobalPool) return false;
-
   // Otherwise get the main function to insert the poolinit calls.
   Function *MainFunc = M.getMainFunction();
   if (MainFunc == 0 || MainFunc->isExternal()) {
@@ -476,75 +397,52 @@
   std::cerr << "Pool allocating " << GlobalHeapNodes.size()
             << " global nodes!\n";
 
-  // If we are putting everything into a single global pool, create it now and
-  // put all globals pool descriptors into it.
-  if (Heuristic == AllInOneGlobalPool) {
-    // Create the global pool descriptor.
-    TheGlobalPD = 
-      new GlobalVariable(PoolDescType, false, GlobalValue::InternalLinkage, 
-                         Constant::getNullValue(PoolDescType), "GlobalPool",&M);
-      
-    // Initialize it on entry to main.
-    Value *ElSize = ConstantUInt::get(Type::UIntTy, 0);
-    new CallInst(PoolInit, make_vector((Value*)TheGlobalPD, ElSize, 0),
-                 "", InsertPt);
-
-    for (hash_set<DSNode*>::iterator I = GlobalHeapNodes.begin(),
-           E = GlobalHeapNodes.end(); I != E; ++I)
-      GlobalNodes[*I] = TheGlobalPD;
 
-    ++NumPools;  // We have one pool.
-    return false;
+  std::vector<DSNode*> NodesToPA(GlobalHeapNodes.begin(),GlobalHeapNodes.end());
+  std::vector<Heuristic::OnePool> ResultPools;
+  CurHeuristic->AssignToPools(NodesToPA, 0, GG, ResultPools);
+
+  // Perform all global assignments as specified.
+  for (unsigned i = 0, e = ResultPools.size(); i != e; ++i) {
+    Heuristic::OnePool &Pool = ResultPools[i];
+    Value *PoolDesc = Pool.PoolDesc;
+    if (PoolDesc == 0)
+      PoolDesc = CreateGlobalPool(Pool.PoolSize);
+    for (unsigned N = 0, e = Pool.NodesInPool.size(); N != e; ++N) {
+      GlobalNodes[Pool.NodesInPool[N]] = PoolDesc;
+      GlobalHeapNodes.erase(Pool.NodesInPool[N]);  // Handled!
+    }
   }
 
-
-  // Loop over all of the pools, creating a new global pool descriptor,
-  // inserting a new entry in GlobalNodes, and inserting a call to poolinit in
-  // main.
+  // Any unallocated DSNodes get null pool descriptor pointers.
   for (hash_set<DSNode*>::iterator I = GlobalHeapNodes.begin(),
          E = GlobalHeapNodes.end(); I != E; ++I) {
-    bool ShouldPoolAlloc = true;
-    switch (Heuristic) {
-    case AllInOneGlobalPool: assert(0 && "Case handled above!");
-    case OnlyOverhead:
-    case AllNodes: break;
-    case NoNodes: ShouldPoolAlloc = false; break;
-    case SmartCoallesceNodes:
-#if 1
-      if ((*I)->isArray() && !(*I)->isNodeCompletelyFolded())
-        ShouldPoolAlloc = false;
-#endif
-      // fall through
-    case CyclicNodes:
-      if (!NodeExistsInCycle(*I))
-        ShouldPoolAlloc = false;
-      break;
-    }
-
-    if (ShouldPoolAlloc) {
-      GlobalVariable *GV =
-        new GlobalVariable(PoolDescType, false, GlobalValue::InternalLinkage, 
-                        Constant::getNullValue(PoolDescType), "GlobalPool",&M);
-      GlobalNodes[*I] = GV;
-      
-      Value *ElSize =
-        ConstantUInt::get(Type::UIntTy, (*I)->getType()->isSized() ? 
-                          TD.getTypeSize((*I)->getType()) : 0);
-      new CallInst(PoolInit, make_vector((Value*)GV, ElSize, 0), "", InsertPt);
-      
-      ++NumPools;
-      if (!(*I)->isNodeCompletelyFolded())
-        ++NumTSPools;
-    } else {
-      GlobalNodes[*I] =
-        Constant::getNullValue(PointerType::get(PoolDescType));
-      ++NumNonprofit;
-    }
+    GlobalNodes[*I] = Constant::getNullValue(PointerType::get(PoolDescType));
+    ++NumNonprofit;
   }
-
+  
   return false;
 }
 
+GlobalVariable *PoolAllocate::CreateGlobalPool(unsigned RecSize) {
+  GlobalVariable *GV =
+    new GlobalVariable(PoolDescType, false, GlobalValue::InternalLinkage, 
+                       Constant::getNullValue(PoolDescType), "GlobalPool",
+                       CurModule);
+
+  Function *MainFunc = CurModule->getMainFunction();
+  assert(MainFunc && "No main in program??");
+
+  BasicBlock::iterator InsertPt = MainFunc->getEntryBlock().begin();
+  while (isa<AllocaInst>(InsertPt)) ++InsertPt;
+
+  Value *ElSize = ConstantUInt::get(Type::UIntTy, RecSize);
+  new CallInst(PoolInit, make_vector((Value*)GV, ElSize, 0), "", InsertPt);
+  ++NumPools;
+  return GV;
+}
+
+
 // 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.
@@ -554,178 +452,35 @@
                                std::map<DSNode*, Value*> &PoolDescriptors) {
   if (NodesToPA.empty()) return;
 
-  // 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();
-
-  switch (Heuristic) {
-  case AllNodes:
-  case NoNodes:
-  case CyclicNodes:
-  case OnlyOverhead:
-    for (unsigned i = 0, e = NodesToPA.size(); i != e; ++i) {
-      DSNode *Node = NodesToPA[i];
-      if (ShouldPoolAllocate(Node)) {
-        // 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 (Node->getType() == Type::VoidTy)
-          std::cerr << "Node collapsing in '" << F.getName() << "'\n";
-        
-        // Update the PoolDescriptors map
-        PoolDescriptors.insert(std::make_pair(Node, AI));
-      } else {
-        PoolDescriptors[Node] =
-          Constant::getNullValue(PointerType::get(PoolDescType));
-        ++NumNonprofit;
-      }
-    }
-    break;
-  case AllInOneGlobalPool:
-    for (unsigned i = 0, e = NodesToPA.size(); i != e; ++i)
-      PoolDescriptors[NodesToPA[i]] = TheGlobalPD;
-    break;
-
-  case SmartCoallesceNodes: {
-    std::set<DSNode*> 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<DSNode*, std::vector<DSNode*> > 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<DSNode*>(*CI))
-          if (NodesToPASet.count(Child))
-            InverseGraph[Child].push_back(Node);
-    }
+  std::vector<Heuristic::OnePool> ResultPools;
+  CurHeuristic->AssignToPools(NodesToPA, &F, *NodesToPA[0]->getParentGraph(),
+                              ResultPools);
 
-    // 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<DSNode*> Visited;
-    std::vector<DSNode*> 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<DSNode*> &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<ConstantPointerNull>(PoolDescriptors[Pred]))
-              PredPool = PoolDescriptors[Pred];
-            // Otherwise, this predecessor has the same pool as a previous one.
-          }
+  std::set<DSNode*> UnallocatedNodes(NodesToPA.begin(), NodesToPA.end());
 
-          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
-}
+  Instruction *InsertPoint = F.front().begin();
 
-/// 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);
+  // Perform all global assignments as specified.
+  for (unsigned i = 0, e = ResultPools.size(); i != e; ++i) {
+    Heuristic::OnePool &Pool = ResultPools[i];
+    Value *PoolDesc = Pool.PoolDesc;
+    if (PoolDesc == 0)
+      // Create a new alloca instruction for the pool.  The poolinit will be
+      // inserted later.
+      PoolDesc = new AllocaInst(PoolDescType, 0, "PD", InsertPoint);
+
+    for (unsigned N = 0, e = Pool.NodesInPool.size(); N != e; ++N) {
+      PoolDescriptors[Pool.NodesInPool[N]] = PoolDesc;
+      UnallocatedNodes.erase(Pool.NodesInPool[N]);  // Handled!
+    }
   }
-  while (isa<AllocaInst>(I)) ++I;
 
-  return new LoadInst(NullGlobal, "nullpd", I);
+  // Any unallocated DSNodes get null pool descriptor pointers.
+  for (std::set<DSNode*>::iterator I = UnallocatedNodes.begin(),
+         E = UnallocatedNodes.end(); I != E; ++I) {
+    PoolDescriptors[*I] =Constant::getNullValue(PointerType::get(PoolDescType));
+    ++NumNonprofit;
+  }
 }
 
 // processFunction - Pool allocate any data structures which are contained in
@@ -753,10 +508,6 @@
                                 GlobalsGraphNodeMapping);
   }
 
-  Value *NullPD = 0;
-  if (Heuristic == OnlyOverhead)
-    NullPD = getDynamicallyNullPool(NewF.front().begin());
-  
   // Loop over all of the nodes which are non-escaping, adding pool-allocatable
   // ones to the NodesToPA vector.
   std::vector<DSNode*> NodesToPA;
@@ -769,8 +520,6 @@
         DSNode *GGN = GlobalsGraphNodeMapping[N].getNode();
         assert(GGN && GlobalNodes[GGN] && "No global node found??");
         FI.PoolDescriptors[N] = GlobalNodes[GGN];
-        if (Heuristic == OnlyOverhead)
-          FI.PoolDescriptors[N] = NullPD;
       } else if (!MarkedNodes.count(N)) {
         // Otherwise, if it was not passed in from outside the function, it must
         // be a local pool!
@@ -797,6 +546,7 @@
   if (!NodesToPA.empty())
     InitializeAndDestroyPools(NewF, NodesToPA, FI.PoolDescriptors,
                               PoolUses, PoolFrees);
+  CurHeuristic->HackFunctionBody(NewF, FI.PoolDescriptors);
 }
 
 template<class IteratorTy>
@@ -848,10 +598,6 @@
                                  std::map<DSNode*, Value*> &PoolDescriptors,
                      std::set<std::pair<AllocaInst*, Instruction*> > &PoolUses,
                      std::set<std::pair<AllocaInst*, CallInst*> > &PoolFrees) {
-  Value *NullPD = 0;
-  if (Heuristic == OnlyOverhead)
-    NullPD = getDynamicallyNullPool(F.front().begin());
-
   TargetData &TD = getAnalysis<TargetData>();
 
   std::vector<Instruction*> PoolInitPoints;
@@ -897,11 +643,6 @@
     if (!HasUse && !HasFree) continue;
     if (!AllocasHandled.insert(PD).second) continue;
 
-    // Arrange to dynamically pass null into all of the pool functions if we are
-    // only checking for overhead.
-    if (Heuristic == OnlyOverhead)
-      PD->replaceAllUsesWith(NullPD);
-
     ++NumPools;
     if (!Node->isNodeCompletelyFolded())
       ++NumTSPools;
@@ -1026,8 +767,9 @@
 
     // Insert the calls to initialize the pool.
     unsigned ElSizeV = 0;
-    if (Node->getType()->isSized())
+    if (!Node->isArray() && Node->getType()->isSized())
       ElSizeV = TD.getTypeSize(Node->getType());
+    if (ElSizeV == 1) ElSizeV = 0;
     Value *ElSize = ConstantUInt::get(Type::UIntTy, ElSizeV);
 
     for (unsigned i = 0, e = PoolInitPoints.size(); i != e; ++i) {


Index: poolalloc/lib/PoolAllocate/PoolAllocate.h
diff -u poolalloc/lib/PoolAllocate/PoolAllocate.h:1.28 poolalloc/lib/PoolAllocate/PoolAllocate.h:1.29
--- poolalloc/lib/PoolAllocate/PoolAllocate.h:1.28	Sun Nov  7 16:19:59 2004
+++ poolalloc/lib/PoolAllocate/PoolAllocate.h	Mon Nov  8 00:18:07 2004
@@ -14,8 +14,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef LLVM_TRANSFORMS_POOLALLOCATE_H
-#define LLVM_TRANSFORMS_POOLALLOCATE_H
+#ifndef POOLALLOCATE_H
+#define POOLALLOCATE_H
 
 #include "llvm/Pass.h"
 #include "llvm/DerivedTypes.h"
@@ -35,6 +35,7 @@
 namespace PA {
 
   class EquivClassGraphs;
+  class Heuristic;
 
   /// FuncInfo - Represent the pool allocation information for one function in
   /// the program.  Note that many functions must actually be cloned in order
@@ -97,6 +98,8 @@
   Function *PoolInit, *PoolDestroy, *PoolAlloc, *PoolRealloc, *PoolFree;
   static const Type *PoolDescPtrTy;
 
+  PA::Heuristic *CurHeuristic;
+
   /// GlobalNodes - For each node (with an H marker) in the globals graph, this
   /// map contains the global variable that holds the pool descriptor for the
   /// node.
@@ -132,6 +135,10 @@
 
   Module *getCurModule() { return CurModule; }
 
+  /// CreateGlobalPool - Create a global pool descriptor, initialize it in main,
+  /// and return a pointer to the global for it.
+  GlobalVariable *CreateGlobalPool(unsigned RecSize);
+
  private:
   
   /// AddPoolPrototypes - Add prototypes for the pool functions to the





More information about the llvm-commits mailing list