This commit was manufactured by cvs2svn to create branch 'poolalloc'. llvm-svn: 7751
diff --git a/poolalloc/include/dsa/DSGraph.h b/poolalloc/include/dsa/DSGraph.h deleted file mode 100644 index 10dca09..0000000 --- a/poolalloc/include/dsa/DSGraph.h +++ /dev/null
@@ -1,308 +0,0 @@ -//===- DSGraph.h - Represent a collection of data structures ----*- C++ -*-===// -// -// This header defines the data structure graph. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_ANALYSIS_DSGRAPH_H -#define LLVM_ANALYSIS_DSGRAPH_H - -#include "llvm/Analysis/DSNode.h" -class GlobalValue; - -//===----------------------------------------------------------------------===// -/// DSGraph - The graph that represents a function. -/// -struct DSGraph { - // Public data-type declarations... - typedef hash_map<Value*, DSNodeHandle> ScalarMapTy; - typedef hash_map<Function*, DSNodeHandle> ReturnNodesTy; - typedef hash_set<const GlobalValue*> GlobalSetTy; - - /// NodeMapTy - This data type is used when cloning one graph into another to - /// keep track of the correspondence between the nodes in the old and new - /// graphs. - typedef hash_map<const DSNode*, DSNodeHandle> NodeMapTy; -private: - DSGraph *GlobalsGraph; // Pointer to the common graph of global objects - bool PrintAuxCalls; // Should this graph print the Aux calls vector? - - std::vector<DSNode*> Nodes; - ScalarMapTy ScalarMap; - - // ReturnNodes - A return value for every function merged into this graph. - // Each DSGraph may have multiple functions merged into it at any time, which - // is used for representing SCCs. - // - ReturnNodesTy ReturnNodes; - - // FunctionCalls - This vector maintains a single entry for each call - // instruction in the current graph. The first entry in the vector is the - // scalar that holds the return value for the call, the second is the function - // scalar being invoked, and the rest are pointer arguments to the function. - // This vector is built by the Local graph and is never modified after that. - // - std::vector<DSCallSite> FunctionCalls; - - // AuxFunctionCalls - This vector contains call sites that have been processed - // by some mechanism. In pratice, the BU Analysis uses this vector to hold - // the _unresolved_ call sites, because it cannot modify FunctionCalls. - // - std::vector<DSCallSite> AuxFunctionCalls; - - // InlinedGlobals - This set records which globals have been inlined from - // other graphs (callers or callees, depending on the pass) into this one. - // - GlobalSetTy InlinedGlobals; - - void operator=(const DSGraph &); // DO NOT IMPLEMENT - -public: - // Create a new, empty, DSGraph. - DSGraph() : GlobalsGraph(0), PrintAuxCalls(false) {} - DSGraph(Function &F, DSGraph *GlobalsGraph); // Compute the local DSGraph - - // Copy ctor - If you want to capture the node mapping between the source and - // destination graph, you may optionally do this by specifying a map to record - // this into. - // - // Note that a copied graph does not retain the GlobalsGraph pointer of the - // source. You need to set a new GlobalsGraph with the setGlobalsGraph - // method. - // - DSGraph(const DSGraph &DSG); - DSGraph(const DSGraph &DSG, NodeMapTy &NodeMap); - ~DSGraph(); - - DSGraph *getGlobalsGraph() const { return GlobalsGraph; } - void setGlobalsGraph(DSGraph *G) { GlobalsGraph = G; } - - // setPrintAuxCalls - If you call this method, the auxillary call vector will - // be printed instead of the standard call vector to the dot file. - // - void setPrintAuxCalls() { PrintAuxCalls = true; } - bool shouldPrintAuxCalls() const { return PrintAuxCalls; } - - /// getNodes - Get a vector of all the nodes in the graph - /// - const std::vector<DSNode*> &getNodes() const { return Nodes; } - std::vector<DSNode*> &getNodes() { return Nodes; } - - /// getFunctionNames - Return a space separated list of the name of the - /// functions in this graph (if any) - std::string getFunctionNames() const; - - /// addNode - Add a new node to the graph. - /// - void addNode(DSNode *N) { Nodes.push_back(N); } - - /// getScalarMap - Get a map that describes what the nodes the scalars in this - /// function point to... - /// - ScalarMapTy &getScalarMap() { return ScalarMap; } - const ScalarMapTy &getScalarMap() const {return ScalarMap;} - - /// getFunctionCalls - Return the list of call sites in the original local - /// graph... - /// - const std::vector<DSCallSite> &getFunctionCalls() const { - return FunctionCalls; - } - - /// getAuxFunctionCalls - Get the call sites as modified by whatever passes - /// have been run. - /// - std::vector<DSCallSite> &getAuxFunctionCalls() { - return AuxFunctionCalls; - } - const std::vector<DSCallSite> &getAuxFunctionCalls() const { - return AuxFunctionCalls; - } - - /// getInlinedGlobals - Get the set of globals that are have been inlined - /// (from callees in BU or from callers in TD) into the current graph. - /// - GlobalSetTy& getInlinedGlobals() { - return InlinedGlobals; - } - - /// getNodeForValue - Given a value that is used or defined in the body of the - /// current function, return the DSNode that it points to. - /// - DSNodeHandle &getNodeForValue(Value *V) { return ScalarMap[V]; } - - const DSNodeHandle &getNodeForValue(Value *V) const { - ScalarMapTy::const_iterator I = ScalarMap.find(V); - assert(I != ScalarMap.end() && - "Use non-const lookup function if node may not be in the map"); - return I->second; - } - - /// getReturnNodes - Return the mapping of functions to their return nodes for - /// this graph. - const ReturnNodesTy &getReturnNodes() const { return ReturnNodes; } - ReturnNodesTy &getReturnNodes() { return ReturnNodes; } - - /// getReturnNodeFor - Return the return node for the specified function. - /// - DSNodeHandle &getReturnNodeFor(Function &F) { - ReturnNodesTy::iterator I = ReturnNodes.find(&F); - assert(I != ReturnNodes.end() && "F not in this DSGraph!"); - return I->second; - } - - const DSNodeHandle &getReturnNodeFor(Function &F) const { - ReturnNodesTy::const_iterator I = ReturnNodes.find(&F); - assert(I != ReturnNodes.end() && "F not in this DSGraph!"); - return I->second; - } - - /// getGraphSize - Return the number of nodes in this graph. - /// - unsigned getGraphSize() const { - return Nodes.size(); - } - - /// print - Print a dot graph to the specified ostream... - /// - void print(std::ostream &O) const; - - /// dump - call print(std::cerr), for use from the debugger... - /// - void dump() const; - - /// viewGraph - Emit a dot graph, run 'dot', run gv on the postscript file, - /// then cleanup. For use from the debugger. - void viewGraph() const; - - void writeGraphToFile(std::ostream &O, const std::string &GraphName) const; - - /// maskNodeTypes - Apply a mask to all of the node types in the graph. This - /// is useful for clearing out markers like Incomplete. - /// - void maskNodeTypes(unsigned Mask) { - for (unsigned i = 0, e = Nodes.size(); i != e; ++i) - Nodes[i]->maskNodeTypes(Mask); - } - void maskIncompleteMarkers() { maskNodeTypes(~DSNode::Incomplete); } - - // markIncompleteNodes - Traverse the graph, identifying nodes that may be - // modified by other functions that have not been resolved yet. This marks - // nodes that are reachable through three sources of "unknownness": - // Global Variables, Function Calls, and Incoming Arguments - // - // For any node that may have unknown components (because something outside - // the scope of current analysis may have modified it), the 'Incomplete' flag - // is added to the NodeType. - // - enum MarkIncompleteFlags { - MarkFormalArgs = 1, IgnoreFormalArgs = 0, - IgnoreGlobals = 2, MarkGlobalsIncomplete = 0, - }; - void markIncompleteNodes(unsigned Flags); - - // removeDeadNodes - Use a reachability analysis to eliminate subgraphs that - // are unreachable. This often occurs because the data structure doesn't - // "escape" into it's caller, and thus should be eliminated from the caller's - // graph entirely. This is only appropriate to use when inlining graphs. - // - enum RemoveDeadNodesFlags { - RemoveUnreachableGlobals = 1, KeepUnreachableGlobals = 0, - }; - void removeDeadNodes(unsigned Flags); - - /// CloneFlags enum - Bits that may be passed into the cloneInto method to - /// specify how to clone the function graph. - enum CloneFlags { - StripAllocaBit = 1 << 0, KeepAllocaBit = 0, - DontCloneCallNodes = 1 << 1, CloneCallNodes = 0, - DontCloneAuxCallNodes = 1 << 2, CloneAuxCallNodes = 0, - StripModRefBits = 1 << 3, KeepModRefBits = 0, - StripIncompleteBit = 1 << 4, KeepIncompleteBit = 0, - }; - -private: - void cloneReachableNodes(const DSNode* Node, - unsigned BitsToClear, - NodeMapTy& OldNodeMap, - NodeMapTy& CompletedNodeMap); - -public: - void updateFromGlobalGraph(); - - void cloneReachableSubgraph(const DSGraph& G, - const hash_set<const DSNode*>& RootNodes, - NodeMapTy& OldNodeMap, - NodeMapTy& CompletedNodeMap, - unsigned CloneFlags = 0); - - /// cloneInto - Clone the specified DSGraph into the current graph. The - /// translated ScalarMap for the old function is filled into the OldValMap - /// member, and the translated ReturnNodes map is returned into ReturnNodes. - /// - /// The CloneFlags member controls various aspects of the cloning process. - /// - void cloneInto(const DSGraph &G, ScalarMapTy &OldValMap, - ReturnNodesTy &OldReturnNodes, NodeMapTy &OldNodeMap, - unsigned CloneFlags = 0); - - /// mergeInGraph - The method is used for merging graphs together. If the - /// argument graph is not *this, it makes a clone of the specified graph, then - /// merges the nodes specified in the call site with the formal arguments in - /// the graph. If the StripAlloca's argument is 'StripAllocaBit' then Alloca - /// markers are removed from nodes. - /// - void mergeInGraph(const DSCallSite &CS, Function &F, const DSGraph &Graph, - unsigned CloneFlags); - - - /// getCallSiteForArguments - Get the arguments and return value bindings for - /// the specified function in the current graph. - /// - DSCallSite getCallSiteForArguments(Function &F) const; - - // Methods for checking to make sure graphs are well formed... - void AssertNodeInGraph(const DSNode *N) const { - assert((!N || find(Nodes.begin(), Nodes.end(), N) != Nodes.end()) && - "AssertNodeInGraph: Node is not in graph!"); - } - void AssertNodeContainsGlobal(const DSNode *N, GlobalValue *GV) const { - assert(std::find(N->getGlobals().begin(), N->getGlobals().end(), GV) != - N->getGlobals().end() && "Global value not in node!"); - } - - void AssertCallSiteInGraph(const DSCallSite &CS) const { - if (CS.isIndirectCall()) - AssertNodeInGraph(CS.getCalleeNode()); - AssertNodeInGraph(CS.getRetVal().getNode()); - for (unsigned j = 0, e = CS.getNumPtrArgs(); j != e; ++j) - AssertNodeInGraph(CS.getPtrArg(j).getNode()); - } - - void AssertCallNodesInGraph() const { - for (unsigned i = 0, e = FunctionCalls.size(); i != e; ++i) - AssertCallSiteInGraph(FunctionCalls[i]); - } - void AssertAuxCallNodesInGraph() const { - for (unsigned i = 0, e = AuxFunctionCalls.size(); i != e; ++i) - AssertCallSiteInGraph(AuxFunctionCalls[i]); - } - - void AssertGraphOK() const; - - /// mergeInGlobalsGraph - This method is useful for clients to incorporate the - /// globals graph into the DS, BU or TD graph for a function. This code - /// retains all globals, i.e., does not delete unreachable globals after they - /// are inlined. - /// - void mergeInGlobalsGraph(); - - /// removeTriviallyDeadNodes - After the graph has been constructed, this - /// method removes all unreachable nodes that are created because they got - /// merged with other nodes in the graph. This is used as the first step of - /// removeDeadNodes. - /// - void removeTriviallyDeadNodes(); -}; - -#endif
diff --git a/poolalloc/include/dsa/DSGraphTraits.h b/poolalloc/include/dsa/DSGraphTraits.h deleted file mode 100644 index 7ea30c0..0000000 --- a/poolalloc/include/dsa/DSGraphTraits.h +++ /dev/null
@@ -1,142 +0,0 @@ -//===- DSGraphTraits.h - Provide generic graph interface --------*- C++ -*-===// -// -// This file provides GraphTraits specializations for the DataStructure graph -// nodes, allowing datastructure graphs to be processed by generic graph -// algorithms. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_ANALYSIS_DSGRAPHTRAITS_H -#define LLVM_ANALYSIS_DSGRAPHTRAITS_H - -#include "llvm/Analysis/DSGraph.h" -#include "Support/GraphTraits.h" -#include "Support/iterator" -#include "Support/STLExtras.h" - -template<typename NodeTy> -class DSNodeIterator : public forward_iterator<const DSNode, ptrdiff_t> { - friend class DSNode; - NodeTy * const Node; - unsigned Offset; - - typedef DSNodeIterator<NodeTy> _Self; - - DSNodeIterator(NodeTy *N) : Node(N), Offset(0) {} // begin iterator - DSNodeIterator(NodeTy *N, bool) : Node(N) { // Create end iterator - Offset = N->getNumLinks() << DS::PointerShift; - if (Offset == 0 && Node->getForwardNode() && - Node->isDeadNode()) // Model Forward link - Offset += DS::PointerSize; - } -public: - DSNodeIterator(const DSNodeHandle &NH) - : Node(NH.getNode()), Offset(NH.getOffset()) {} - - bool operator==(const _Self& x) const { - return Offset == x.Offset; - } - bool operator!=(const _Self& x) const { return !operator==(x); } - - const _Self &operator=(const _Self &I) { - assert(I.Node == Node && "Cannot assign iterators to two different nodes!"); - Offset = I.Offset; - return *this; - } - - pointer operator*() const { - if (Node->isDeadNode()) - return Node->getForwardNode(); - else - return Node->getLink(Offset).getNode(); - } - pointer operator->() const { return operator*(); } - - _Self& operator++() { // Preincrement - Offset += (1 << DS::PointerShift); - return *this; - } - _Self operator++(int) { // Postincrement - _Self tmp = *this; ++*this; return tmp; - } - - unsigned getOffset() const { return Offset; } - const DSNode *getNode() const { return Node; } -}; - -// Provide iterators for DSNode... -inline DSNode::iterator DSNode::begin() { - return DSNode::iterator(this); -} -inline DSNode::iterator DSNode::end() { - return DSNode::iterator(this, false); -} -inline DSNode::const_iterator DSNode::begin() const { - return DSNode::const_iterator(this); -} -inline DSNode::const_iterator DSNode::end() const { - return DSNode::const_iterator(this, false); -} - -template <> struct GraphTraits<DSNode*> { - typedef DSNode NodeType; - typedef DSNode::iterator ChildIteratorType; - - static NodeType *getEntryNode(NodeType *N) { return N; } - static ChildIteratorType child_begin(NodeType *N) { return N->begin(); } - static ChildIteratorType child_end(NodeType *N) { return N->end(); } -}; - -template <> struct GraphTraits<const DSNode*> { - typedef const DSNode NodeType; - typedef DSNode::const_iterator ChildIteratorType; - - static NodeType *getEntryNode(NodeType *N) { return N; } - static ChildIteratorType child_begin(NodeType *N) { return N->begin(); } - static ChildIteratorType child_end(NodeType *N) { return N->end(); } -}; - -static DSNode &dereference ( DSNode *N) { return *N; } -static const DSNode &dereferenceC(const DSNode *N) { return *N; } - -template <> struct GraphTraits<DSGraph*> { - typedef DSNode NodeType; - typedef DSNode::iterator ChildIteratorType; - - typedef std::pointer_to_unary_function<DSNode *, DSNode&> DerefFun; - - // nodes_iterator/begin/end - Allow iteration over all nodes in the graph - typedef mapped_iterator<std::vector<DSNode*>::iterator, - DerefFun> nodes_iterator; - static nodes_iterator nodes_begin(DSGraph *G) { - return map_iterator(G->getNodes().begin(), DerefFun(dereference)); - } - static nodes_iterator nodes_end(DSGraph *G) { - return map_iterator(G->getNodes().end(), DerefFun(dereference)); - } - - static ChildIteratorType child_begin(NodeType *N) { return N->begin(); } - static ChildIteratorType child_end(NodeType *N) { return N->end(); } -}; - -template <> struct GraphTraits<const DSGraph*> { - typedef const DSNode NodeType; - typedef DSNode::const_iterator ChildIteratorType; - - typedef std::pointer_to_unary_function<const DSNode *,const DSNode&> DerefFun; - - // nodes_iterator/begin/end - Allow iteration over all nodes in the graph - typedef mapped_iterator<std::vector<DSNode*>::const_iterator, - DerefFun> nodes_iterator; - static nodes_iterator nodes_begin(const DSGraph *G) { - return map_iterator(G->getNodes().begin(), DerefFun(dereferenceC)); - } - static nodes_iterator nodes_end(const DSGraph *G) { - return map_iterator(G->getNodes().end(), DerefFun(dereferenceC)); - } - - static ChildIteratorType child_begin(const NodeType *N) { return N->begin(); } - static ChildIteratorType child_end(const NodeType *N) { return N->end(); } -}; - -#endif
diff --git a/poolalloc/include/dsa/DSNode.h b/poolalloc/include/dsa/DSNode.h deleted file mode 100644 index ff1d7d6..0000000 --- a/poolalloc/include/dsa/DSNode.h +++ /dev/null
@@ -1,372 +0,0 @@ -//===- DSNode.h - Node definition for datastructure graphs ------*- C++ -*-===// -// -// Data structure graph nodes and some implementation of DSNodeHandle. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_ANALYSIS_DSNODE_H -#define LLVM_ANALYSIS_DSNODE_H - -#include "llvm/Analysis/DSSupport.h" - -template<typename BaseType> -class DSNodeIterator; // Data structure graph traversal iterator - -//===----------------------------------------------------------------------===// -/// DSNode - Data structure node class -/// -/// This class represents an untyped memory object of Size bytes. It keeps -/// track of any pointers that have been stored into the object as well as the -/// different types represented in this object. -/// -class DSNode { - /// NumReferrers - The number of DSNodeHandles pointing to this node... if - /// this is a forwarding node, then this is the number of node handles which - /// are still forwarding over us. - /// - unsigned NumReferrers; - - /// ForwardNH - This NodeHandle contain the node (and offset into the node) - /// that this node really is. When nodes get folded together, the node to be - /// eliminated has these fields filled in, otherwise ForwardNH.getNode() is - /// null. - DSNodeHandle ForwardNH; - - /// Size - The current size of the node. This should be equal to the size of - /// the current type record. - /// - unsigned Size; - - /// ParentGraph - The graph this node is currently embedded into. - /// - DSGraph *ParentGraph; - - /// Ty - Keep track of the current outer most type of this object, in addition - /// to whether or not it has been indexed like an array or not. If the - /// isArray bit is set, the node cannot grow. - /// - const Type *Ty; // The type itself... - - /// Links - Contains one entry for every sizeof(void*) bytes in this memory - /// object. Note that if the node is not a multiple of size(void*) bytes - /// large, that there is an extra entry for the "remainder" of the node as - /// well. For this reason, nodes of 1 byte in size do have one link. - /// - std::vector<DSNodeHandle> Links; - - /// Globals - The list of global values that are merged into this node. - /// - std::vector<GlobalValue*> Globals; - - void operator=(const DSNode &); // DO NOT IMPLEMENT - DSNode(const DSNode &); // DO NOT IMPLEMENT -public: - enum NodeTy { - ShadowNode = 0, // Nothing is known about this node... - AllocaNode = 1 << 0, // This node was allocated with alloca - HeapNode = 1 << 1, // This node was allocated with malloc - GlobalNode = 1 << 2, // This node was allocated by a global var decl - UnknownNode = 1 << 3, // This node points to unknown allocated memory - Incomplete = 1 << 4, // This node may not be complete - - Modified = 1 << 5, // This node is modified in this context - Read = 1 << 6, // This node is read in this context - - Array = 1 << 7, // This node is treated like an array - //#ifndef NDEBUG - DEAD = 1 << 8, // This node is dead and should not be pointed to - //#endif - - Composition = AllocaNode | HeapNode | GlobalNode | UnknownNode, - }; - - /// NodeType - A union of the above bits. "Shadow" nodes do not add any flags - /// to the nodes in the data structure graph, so it is possible to have nodes - /// with a value of 0 for their NodeType. - /// -private: - unsigned short NodeType; -public: - - DSNode(const Type *T, DSGraph *G); - DSNode(const DSNode &, DSGraph *G); - - ~DSNode() { - dropAllReferences(); - assert(hasNoReferrers() && "Referrers to dead node exist!"); - } - - // Iterator for graph interface... Defined in DSGraphTraits.h - typedef DSNodeIterator<DSNode> iterator; - typedef DSNodeIterator<const DSNode> const_iterator; - inline iterator begin(); - inline iterator end(); - inline const_iterator begin() const; - inline const_iterator end() const; - - //===-------------------------------------------------- - // Accessors - - /// getSize - Return the maximum number of bytes occupied by this object... - /// - unsigned getSize() const { return Size; } - - // getType - Return the node type of this object... - const Type *getType() const { return Ty; } - bool isArray() const { return NodeType & Array; } - - /// hasNoReferrers - Return true if nothing is pointing to this node at all. - /// - bool hasNoReferrers() const { return getNumReferrers() == 0; } - - /// getNumReferrers - This method returns the number of referrers to the - /// current node. Note that if this node is a forwarding node, this will - /// return the number of nodes forwarding over the node! - unsigned getNumReferrers() const { return NumReferrers; } - - DSGraph *getParentGraph() const { return ParentGraph; } - void setParentGraph(DSGraph *G) { ParentGraph = G; } - - /// getForwardNode - This method returns the node that this node is forwarded - /// to, if any. - DSNode *getForwardNode() const { return ForwardNH.getNode(); } - void stopForwarding() { - assert(!ForwardNH.isNull() && - "Node isn't forwarding, cannot stopForwarding!"); - ForwardNH.setNode(0); - } - - /// hasLink - Return true if this memory object has a link in slot #LinkNo - /// - bool hasLink(unsigned Offset) const { - assert((Offset & ((1 << DS::PointerShift)-1)) == 0 && - "Pointer offset not aligned correctly!"); - unsigned Index = Offset >> DS::PointerShift; - assert(Index < Links.size() && "Link index is out of range!"); - return Links[Index].getNode(); - } - - /// getLink - Return the link at the specified offset. - DSNodeHandle &getLink(unsigned Offset) { - assert((Offset & ((1 << DS::PointerShift)-1)) == 0 && - "Pointer offset not aligned correctly!"); - unsigned Index = Offset >> DS::PointerShift; - assert(Index < Links.size() && "Link index is out of range!"); - return Links[Index]; - } - const DSNodeHandle &getLink(unsigned Offset) const { - assert((Offset & ((1 << DS::PointerShift)-1)) == 0 && - "Pointer offset not aligned correctly!"); - unsigned Index = Offset >> DS::PointerShift; - assert(Index < Links.size() && "Link index is out of range!"); - return Links[Index]; - } - - /// getNumLinks - Return the number of links in a node... - /// - unsigned getNumLinks() const { return Links.size(); } - - /// mergeTypeInfo - This method merges the specified type into the current - /// node at the specified offset. This may update the current node's type - /// record if this gives more information to the node, it may do nothing to - /// the node if this information is already known, or it may merge the node - /// completely (and return true) if the information is incompatible with what - /// is already known. - /// - /// This method returns true if the node is completely folded, otherwise - /// false. - /// - bool mergeTypeInfo(const Type *Ty, unsigned Offset, - bool FoldIfIncompatible = true); - - /// foldNodeCompletely - If we determine that this node has some funny - /// behavior happening to it that we cannot represent, we fold it down to a - /// single, completely pessimistic, node. This node is represented as a - /// single byte with a single TypeEntry of "void" with isArray = true. - /// - void foldNodeCompletely(); - - /// isNodeCompletelyFolded - Return true if this node has been completely - /// folded down to something that can never be expanded, effectively losing - /// all of the field sensitivity that may be present in the node. - /// - bool isNodeCompletelyFolded() const; - - /// setLink - Set the link at the specified offset to the specified - /// NodeHandle, replacing what was there. It is uncommon to use this method, - /// instead one of the higher level methods should be used, below. - /// - void setLink(unsigned Offset, const DSNodeHandle &NH) { - assert((Offset & ((1 << DS::PointerShift)-1)) == 0 && - "Pointer offset not aligned correctly!"); - unsigned Index = Offset >> DS::PointerShift; - assert(Index < Links.size() && "Link index is out of range!"); - Links[Index] = NH; - } - - /// getPointerSize - Return the size of a pointer for the current target. - /// - unsigned getPointerSize() const { return DS::PointerSize; } - - /// addEdgeTo - Add an edge from the current node to the specified node. This - /// can cause merging of nodes in the graph. - /// - void addEdgeTo(unsigned Offset, const DSNodeHandle &NH); - - /// mergeWith - Merge this node and the specified node, moving all links to - /// and from the argument node into the current node, deleting the node - /// argument. Offset indicates what offset the specified node is to be merged - /// into the current node. - /// - /// The specified node may be a null pointer (in which case, nothing happens). - /// - void mergeWith(const DSNodeHandle &NH, unsigned Offset); - - /// addGlobal - Add an entry for a global value to the Globals list. This - /// also marks the node with the 'G' flag if it does not already have it. - /// - void addGlobal(GlobalValue *GV); - const std::vector<GlobalValue*> &getGlobals() const { return Globals; } - - /// maskNodeTypes - Apply a mask to the node types bitfield. - /// - void maskNodeTypes(unsigned Mask) { - NodeType &= Mask; - } - - /// getNodeFlags - Return all of the flags set on the node. If the DEAD flag - /// is set, hide it from the caller. - unsigned getNodeFlags() const { return NodeType & ~DEAD; } - - bool isAllocaNode() const { return NodeType & AllocaNode; } - bool isHeapNode() const { return NodeType & HeapNode; } - bool isGlobalNode() const { return NodeType & GlobalNode; } - bool isUnknownNode() const { return NodeType & UnknownNode; } - - bool isModified() const { return NodeType & Modified; } - bool isRead() const { return NodeType & Read; } - - bool isIncomplete() const { return NodeType & Incomplete; } - bool isComplete() const { return !isIncomplete(); } - bool isDeadNode() const { return NodeType & DEAD; } - - DSNode *setAllocaNodeMarker() { NodeType |= AllocaNode; return this; } - DSNode *setHeapNodeMarker() { NodeType |= HeapNode; return this; } - DSNode *setGlobalNodeMarker() { NodeType |= GlobalNode; return this; } - DSNode *setUnknownNodeMarker() { NodeType |= UnknownNode; return this; } - - DSNode *setIncompleteMarker() { NodeType |= Incomplete; return this; } - DSNode *setModifiedMarker() { NodeType |= Modified; return this; } - DSNode *setReadMarker() { NodeType |= Read; return this; } - - void makeNodeDead() { - Globals.clear(); - assert(hasNoReferrers() && "Dead node shouldn't have refs!"); - NodeType = DEAD; - } - - /// forwardNode - Mark this node as being obsolete, and all references to it - /// should be forwarded to the specified node and offset. - /// - void forwardNode(DSNode *To, unsigned Offset); - - void print(std::ostream &O, const DSGraph *G) const; - void dump() const; - - void assertOK() const; - - void dropAllReferences() { - Links.clear(); - if (!ForwardNH.isNull()) - ForwardNH.setNode(0); - } - - /// remapLinks - Change all of the Links in the current node according to the - /// specified mapping. - void remapLinks(hash_map<const DSNode*, DSNodeHandle> &OldNodeMap); - - /// markReachableNodes - This method recursively traverses the specified - /// DSNodes, marking any nodes which are reachable. All reachable nodes it - /// adds to the set, which allows it to only traverse visited nodes once. - /// - void markReachableNodes(hash_set<DSNode*> &ReachableNodes); - -private: - friend class DSNodeHandle; - - // static mergeNodes - Helper for mergeWith() - static void MergeNodes(DSNodeHandle& CurNodeH, DSNodeHandle& NH); -}; - - -//===----------------------------------------------------------------------===// -// Define inline DSNodeHandle functions that depend on the definition of DSNode -// -inline DSNode *DSNodeHandle::getNode() const { - assert((!N || Offset < N->Size || (N->Size == 0 && Offset == 0) || - !N->ForwardNH.isNull()) && "Node handle offset out of range!"); - if (!N || N->ForwardNH.isNull()) - return N; - - return HandleForwarding(); -} - -inline void DSNodeHandle::setNode(DSNode *n) { - assert(!n || !n->getForwardNode() && "Cannot set node to a forwarded node!"); - if (N) N->NumReferrers--; - N = n; - if (N) { - N->NumReferrers++; - if (Offset >= N->Size) { - assert((Offset == 0 || N->Size == 1) && - "Pointer to non-collapsed node with invalid offset!"); - Offset = 0; - } - } - assert(!N || ((N->NodeType & DSNode::DEAD) == 0)); - assert((!N || Offset < N->Size || (N->Size == 0 && Offset == 0) || - !N->ForwardNH.isNull()) && "Node handle offset out of range!"); -} - -inline bool DSNodeHandle::hasLink(unsigned Num) const { - assert(N && "DSNodeHandle does not point to a node yet!"); - return getNode()->hasLink(Num+Offset); -} - - -/// getLink - Treat this current node pointer as a pointer to a structure of -/// some sort. This method will return the pointer a mem[this+Num] -/// -inline const DSNodeHandle &DSNodeHandle::getLink(unsigned Off) const { - assert(N && "DSNodeHandle does not point to a node yet!"); - return getNode()->getLink(Offset+Off); -} -inline DSNodeHandle &DSNodeHandle::getLink(unsigned Off) { - assert(N && "DSNodeHandle does not point to a node yet!"); - return getNode()->getLink(Off+Offset); -} - -inline void DSNodeHandle::setLink(unsigned Off, const DSNodeHandle &NH) { - assert(N && "DSNodeHandle does not point to a node yet!"); - getNode()->setLink(Off+Offset, NH); -} - -/// addEdgeTo - Add an edge from the current node to the specified node. This -/// can cause merging of nodes in the graph. -/// -inline void DSNodeHandle::addEdgeTo(unsigned Off, const DSNodeHandle &Node) { - assert(N && "DSNodeHandle does not point to a node yet!"); - getNode()->addEdgeTo(Off+Offset, Node); -} - -/// mergeWith - Merge the logical node pointed to by 'this' with the node -/// pointed to by 'N'. -/// -inline void DSNodeHandle::mergeWith(const DSNodeHandle &Node) { - if (N != 0) - getNode()->mergeWith(Node, Offset); - else // No node to merge with, so just point to Node - *this = Node; -} - -#endif
diff --git a/poolalloc/include/dsa/DSSupport.h b/poolalloc/include/dsa/DSSupport.h deleted file mode 100644 index aff7abc..0000000 --- a/poolalloc/include/dsa/DSSupport.h +++ /dev/null
@@ -1,288 +0,0 @@ -//===- DSSupport.h - Support for datastructure graphs -----------*- C++ -*-===// -// -// Support for graph nodes, call sites, and types. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_ANALYSIS_DSSUPPORT_H -#define LLVM_ANALYSIS_DSSUPPORT_H - -#include <vector> -#include <functional> -#include <string> -#include <cassert> -#include "Support/hash_set" - -class Function; -class CallInst; -class Value; -class GlobalValue; -class Type; - -class DSNode; // Each node in the graph -class DSGraph; // A graph for a function - -namespace DS { // FIXME: After the paper, this should get cleaned up - enum { PointerShift = 3, // 64bit ptrs = 3, 32 bit ptrs = 2 - PointerSize = 1 << PointerShift - }; - - // isPointerType - Return true if this first class type is big enough to hold - // a pointer. - // - bool isPointerType(const Type *Ty); -}; - -//===----------------------------------------------------------------------===// -/// DSNodeHandle - Implement a "handle" to a data structure node that takes care -/// of all of the add/un'refing of the node to prevent the backpointers in the -/// graph from getting out of date. This class represents a "pointer" in the -/// graph, whose destination is an indexed offset into a node. -/// -/// Note: some functions that are marked as inline in DSNodeHandle are actually -/// defined in DSNode.h because they need knowledge of DSNode operation. Putting -/// them in a CPP file wouldn't help making them inlined and keeping DSNode and -/// DSNodeHandle (and friends) in one file complicates things. -/// -class DSNodeHandle { - mutable DSNode *N; - mutable unsigned Offset; - void operator==(const DSNode *N); // DISALLOW, use to promote N to nodehandle -public: - // Allow construction, destruction, and assignment... - DSNodeHandle(DSNode *n = 0, unsigned offs = 0) : N(0), Offset(offs) { - setNode(n); - } - DSNodeHandle(const DSNodeHandle &H) : N(0), Offset(0) { - setNode(H.getNode()); - Offset = H.Offset; // Must read offset AFTER the getNode() - } - ~DSNodeHandle() { setNode((DSNode*)0); } - DSNodeHandle &operator=(const DSNodeHandle &H) { - if (&H == this) return *this; // Don't set offset to 0 if self assigning. - Offset = 0; setNode(H.getNode()); Offset = H.Offset; - return *this; - } - - bool operator<(const DSNodeHandle &H) const { // Allow sorting - return getNode() < H.getNode() || (N == H.N && Offset < H.Offset); - } - bool operator>(const DSNodeHandle &H) const { return H < *this; } - bool operator==(const DSNodeHandle &H) const { // Allow comparison - return getNode() == H.getNode() && Offset == H.Offset; - } - bool operator!=(const DSNodeHandle &H) const { return !operator==(H); } - - inline void swap(DSNodeHandle &NH) { - std::swap(Offset, NH.Offset); - std::swap(N, NH.N); - } - - /// isNull - Check to see if getNode() == 0, without going through the trouble - /// of checking to see if we are forwarding... - bool isNull() const { return N == 0; } - - // Allow explicit conversion to DSNode... - inline DSNode *getNode() const; // Defined inline in DSNode.h - unsigned getOffset() const { return Offset; } - - inline void setNode(DSNode *N); // Defined inline in DSNode.h - void setOffset(unsigned O) { - //assert((!N || Offset < N->Size || (N->Size == 0 && Offset == 0) || - // !N->ForwardNH.isNull()) && "Node handle offset out of range!"); - //assert((!N || O < N->Size || (N->Size == 0 && O == 0) || - // !N->ForwardNH.isNull()) && "Node handle offset out of range!"); - Offset = O; - } - - void addEdgeTo(unsigned LinkNo, const DSNodeHandle &N); - void addEdgeTo(const DSNodeHandle &N) { addEdgeTo(0, N); } - - /// mergeWith - Merge the logical node pointed to by 'this' with the node - /// pointed to by 'N'. - /// - void mergeWith(const DSNodeHandle &N); - - // hasLink - Return true if there is a link at the specified offset... - inline bool hasLink(unsigned Num) const; - - /// getLink - Treat this current node pointer as a pointer to a structure of - /// some sort. This method will return the pointer a mem[this+Num] - /// - inline const DSNodeHandle &getLink(unsigned Num) const; - inline DSNodeHandle &getLink(unsigned Num); - - inline void setLink(unsigned Num, const DSNodeHandle &NH); -private: - DSNode *HandleForwarding() const; -}; - -namespace std { - inline void swap(DSNodeHandle &NH1, DSNodeHandle &NH2) { NH1.swap(NH2); } -} - -//===----------------------------------------------------------------------===// -/// DSCallSite - Representation of a call site via its call instruction, -/// the DSNode handle for the callee function (or function pointer), and -/// the DSNode handles for the function arguments. -/// -class DSCallSite { - CallInst *Inst; // Actual call site - Function *CalleeF; // The function called (direct call) - DSNodeHandle CalleeN; // The function node called (indirect call) - DSNodeHandle RetVal; // Returned value - std::vector<DSNodeHandle> CallArgs;// The pointer arguments - - static void InitNH(DSNodeHandle &NH, const DSNodeHandle &Src, - const hash_map<const DSNode*, DSNode*> &NodeMap) { - if (DSNode *N = Src.getNode()) { - hash_map<const DSNode*, DSNode*>::const_iterator I = NodeMap.find(N); - assert(I != NodeMap.end() && "Not not in mapping!"); - - NH.setOffset(Src.getOffset()); - NH.setNode(I->second); - } - } - - static void InitNH(DSNodeHandle &NH, const DSNodeHandle &Src, - const hash_map<const DSNode*, DSNodeHandle> &NodeMap) { - if (DSNode *N = Src.getNode()) { - hash_map<const DSNode*, DSNodeHandle>::const_iterator I = NodeMap.find(N); - assert(I != NodeMap.end() && "Not not in mapping!"); - - NH.setOffset(Src.getOffset()+I->second.getOffset()); - NH.setNode(I->second.getNode()); - } - } - - DSCallSite(); // DO NOT IMPLEMENT -public: - /// Constructor. Note - This ctor destroys the argument vector passed in. On - /// exit, the argument vector is empty. - /// - DSCallSite(CallInst &inst, const DSNodeHandle &rv, DSNode *Callee, - std::vector<DSNodeHandle> &Args) - : Inst(&inst), CalleeF(0), CalleeN(Callee), RetVal(rv) { - assert(Callee && "Null callee node specified for call site!"); - Args.swap(CallArgs); - } - DSCallSite(CallInst &inst, const DSNodeHandle &rv, Function *Callee, - std::vector<DSNodeHandle> &Args) - : Inst(&inst), CalleeF(Callee), RetVal(rv) { - assert(Callee && "Null callee function specified for call site!"); - Args.swap(CallArgs); - } - - DSCallSite(const DSCallSite &DSCS) // Simple copy ctor - : Inst(DSCS.Inst), CalleeF(DSCS.CalleeF), CalleeN(DSCS.CalleeN), - RetVal(DSCS.RetVal), CallArgs(DSCS.CallArgs) {} - - /// Mapping copy constructor - This constructor takes a preexisting call site - /// to copy plus a map that specifies how the links should be transformed. - /// This is useful when moving a call site from one graph to another. - /// - template<typename MapTy> - DSCallSite(const DSCallSite &FromCall, const MapTy &NodeMap) { - Inst = FromCall.Inst; - InitNH(RetVal, FromCall.RetVal, NodeMap); - InitNH(CalleeN, FromCall.CalleeN, NodeMap); - CalleeF = FromCall.CalleeF; - - CallArgs.resize(FromCall.CallArgs.size()); - for (unsigned i = 0, e = FromCall.CallArgs.size(); i != e; ++i) - InitNH(CallArgs[i], FromCall.CallArgs[i], NodeMap); - } - - const DSCallSite &operator=(const DSCallSite &RHS) { - Inst = RHS.Inst; - CalleeF = RHS.CalleeF; - CalleeN = RHS.CalleeN; - RetVal = RHS.RetVal; - CallArgs = RHS.CallArgs; - return *this; - } - - /// isDirectCall - Return true if this call site is a direct call of the - /// function specified by getCalleeFunc. If not, it is an indirect call to - /// the node specified by getCalleeNode. - /// - bool isDirectCall() const { return CalleeF != 0; } - bool isIndirectCall() const { return !isDirectCall(); } - - - // Accessor functions... - Function &getCaller() const; - CallInst &getCallInst() const { return *Inst; } - DSNodeHandle &getRetVal() { return RetVal; } - const DSNodeHandle &getRetVal() const { return RetVal; } - - DSNode *getCalleeNode() const { - assert(!CalleeF && CalleeN.getNode()); return CalleeN.getNode(); - } - Function *getCalleeFunc() const { - assert(!CalleeN.getNode() && CalleeF); return CalleeF; - } - - unsigned getNumPtrArgs() const { return CallArgs.size(); } - - DSNodeHandle &getPtrArg(unsigned i) { - assert(i < CallArgs.size() && "Argument to getPtrArgNode is out of range!"); - return CallArgs[i]; - } - const DSNodeHandle &getPtrArg(unsigned i) const { - assert(i < CallArgs.size() && "Argument to getPtrArgNode is out of range!"); - return CallArgs[i]; - } - - void swap(DSCallSite &CS) { - if (this != &CS) { - std::swap(Inst, CS.Inst); - std::swap(RetVal, CS.RetVal); - std::swap(CalleeN, CS.CalleeN); - std::swap(CalleeF, CS.CalleeF); - std::swap(CallArgs, CS.CallArgs); - } - } - - // MergeWith - Merge the return value and parameters of the these two call - // sites. - void mergeWith(DSCallSite &CS) { - getRetVal().mergeWith(CS.getRetVal()); - unsigned MinArgs = getNumPtrArgs(); - if (CS.getNumPtrArgs() < MinArgs) MinArgs = CS.getNumPtrArgs(); - - for (unsigned a = 0; a != MinArgs; ++a) - getPtrArg(a).mergeWith(CS.getPtrArg(a)); - } - - /// markReachableNodes - This method recursively traverses the specified - /// DSNodes, marking any nodes which are reachable. All reachable nodes it - /// adds to the set, which allows it to only traverse visited nodes once. - /// - void markReachableNodes(hash_set<DSNode*> &Nodes); - - bool operator<(const DSCallSite &CS) const { - if (isDirectCall()) { // This must sort by callee first! - if (CS.isIndirectCall()) return true; - if (CalleeF < CS.CalleeF) return true; - if (CalleeF > CS.CalleeF) return false; - } else { - if (CS.isDirectCall()) return false; - if (CalleeN < CS.CalleeN) return true; - if (CalleeN > CS.CalleeN) return false; - } - if (RetVal < CS.RetVal) return true; - if (RetVal > CS.RetVal) return false; - return CallArgs < CS.CallArgs; - } - - bool operator==(const DSCallSite &CS) const { - return RetVal == CS.RetVal && CalleeN == CS.CalleeN && - CalleeF == CS.CalleeF && CallArgs == CS.CallArgs; - } -}; - -namespace std { - inline void swap(DSCallSite &CS1, DSCallSite &CS2) { CS1.swap(CS2); } -} -#endif
diff --git a/poolalloc/include/dsa/DataStructure.h b/poolalloc/include/dsa/DataStructure.h deleted file mode 100644 index d8f30d2..0000000 --- a/poolalloc/include/dsa/DataStructure.h +++ /dev/null
@@ -1,176 +0,0 @@ -//===- DataStructure.h - Build data structure graphs ------------*- C++ -*-===// -// -// Implement the LLVM data structure analysis library. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_ANALYSIS_DATA_STRUCTURE_H -#define LLVM_ANALYSIS_DATA_STRUCTURE_H - -#include "llvm/Pass.h" -#include "Support/hash_set" - -class Type; -class CallInst; -class DSGraph; -class DSNode; -class DSCallSite; - -// FIXME: move this stuff to a private header -namespace DataStructureAnalysis { - // isPointerType - Return true if this first class type is big enough to hold - // a pointer. - // - bool isPointerType(const Type *Ty); -} - - -// LocalDataStructures - The analysis that computes the local data structure -// graphs for all of the functions in the program. -// -// FIXME: This should be a Function pass that can be USED by a Pass, and would -// be automatically preserved. Until we can do that, this is a Pass. -// -class LocalDataStructures : public Pass { - // DSInfo, one graph for each function - hash_map<Function*, DSGraph*> DSInfo; - DSGraph *GlobalsGraph; -public: - ~LocalDataStructures() { releaseMemory(); } - - virtual bool run(Module &M); - - bool hasGraph(const Function &F) const { - return DSInfo.find(const_cast<Function*>(&F)) != DSInfo.end(); - } - - // getDSGraph - Return the data structure graph for the specified function. - DSGraph &getDSGraph(const Function &F) const { - hash_map<Function*, DSGraph*>::const_iterator I = - DSInfo.find(const_cast<Function*>(&F)); - assert(I != DSInfo.end() && "Function not in module!"); - return *I->second; - } - - DSGraph &getGlobalsGraph() const { return *GlobalsGraph; } - - // print - Print out the analysis results... - void print(std::ostream &O, const Module *M) const; - - // If the pass pipeline is done with this pass, we can release our memory... - virtual void releaseMemory(); - - // getAnalysisUsage - This obviously provides a data structure graph. - virtual void getAnalysisUsage(AnalysisUsage &AU) const { - AU.setPreservesAll(); - } -}; - - -// BUDataStructures - The analysis that computes the interprocedurally closed -// data structure graphs for all of the functions in the program. This pass -// only performs a "Bottom Up" propagation (hence the name). -// -class BUDataStructures : public Pass { - // DSInfo, one graph for each function - hash_map<Function*, DSGraph*> DSInfo; - DSGraph *GlobalsGraph; - hash_multimap<CallInst*, Function*> ActualCallees; -public: - ~BUDataStructures() { releaseMemory(); } - - virtual bool run(Module &M); - - bool hasGraph(const Function &F) const { - return DSInfo.find(const_cast<Function*>(&F)) != DSInfo.end(); - } - - // getDSGraph - Return the data structure graph for the specified function. - DSGraph &getDSGraph(const Function &F) const { - hash_map<Function*, DSGraph*>::const_iterator I = - DSInfo.find(const_cast<Function*>(&F)); - assert(I != DSInfo.end() && "Function not in module!"); - return *I->second; - } - - DSGraph &getGlobalsGraph() const { return *GlobalsGraph; } - - // print - Print out the analysis results... - void print(std::ostream &O, const Module *M) const; - - // If the pass pipeline is done with this pass, we can release our memory... - virtual void releaseMemory(); - - virtual void getAnalysisUsage(AnalysisUsage &AU) const { - AU.setPreservesAll(); - AU.addRequired<LocalDataStructures>(); - } - - typedef hash_multimap<CallInst*, Function*> ActualCalleesTy; - const ActualCalleesTy &getActualCallees() const { - return ActualCallees; - } - -private: - void calculateGraph(DSGraph &G); - - void calculateReachableGraphs(Function *F); - - - DSGraph &getOrCreateGraph(Function *F); - - unsigned calculateGraphs(Function *F, std::vector<Function*> &Stack, - unsigned &NextID, - hash_map<Function*, unsigned> &ValMap); -}; - - -// TDDataStructures - Analysis that computes new data structure graphs -// for each function using the closed graphs for the callers computed -// by the bottom-up pass. -// -class TDDataStructures : public Pass { - // DSInfo, one graph for each function - hash_map<Function*, DSGraph*> DSInfo; - hash_set<Function*> ArgsRemainIncomplete; - DSGraph *GlobalsGraph; -public: - ~TDDataStructures() { releaseMyMemory(); } - - virtual bool run(Module &M); - - bool hasGraph(const Function &F) const { - return DSInfo.find(const_cast<Function*>(&F)) != DSInfo.end(); - } - - // getDSGraph - Return the data structure graph for the specified function. - DSGraph &getDSGraph(const Function &F) const { - hash_map<Function*, DSGraph*>::const_iterator I = - DSInfo.find(const_cast<Function*>(&F)); - assert(I != DSInfo.end() && "Function not in module!"); - return *I->second; - } - - DSGraph &getGlobalsGraph() const { return *GlobalsGraph; } - - // print - Print out the analysis results... - void print(std::ostream &O, const Module *M) const; - - // If the pass pipeline is done with this pass, we can release our memory... - virtual void releaseMyMemory(); - - // getAnalysisUsage - This obviously provides a data structure graph. - virtual void getAnalysisUsage(AnalysisUsage &AU) const { - AU.setPreservesAll(); - AU.addRequired<BUDataStructures>(); - } - -private: - void inlineGraphIntoCallees(DSGraph &G); - DSGraph &getOrCreateDSGraph(Function &F); - void ComputePostOrder(Function &F, hash_set<DSGraph*> &Visited, - std::vector<DSGraph*> &PostOrder, - const BUDataStructures::ActualCalleesTy &ActualCallees); -}; - -#endif
diff --git a/poolalloc/include/poolalloc/PoolAllocate.h b/poolalloc/include/poolalloc/PoolAllocate.h deleted file mode 100644 index b6806c1..0000000 --- a/poolalloc/include/poolalloc/PoolAllocate.h +++ /dev/null
@@ -1,156 +0,0 @@ -//===-- PoolAllocate.h - Pool allocation pass -------------------*- C++ -*-===// -// -// This transform changes programs so that disjoint data structures are -// allocated out of different pools of memory, increasing locality. This header -// file exposes information about the pool allocation itself so that follow-on -// passes may extend or use the pool allocation for analysis. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_TRANSFORMS_POOLALLOCATE_H -#define LLVM_TRANSFORMS_POOLALLOCATE_H - -#include "llvm/Pass.h" -#include "Support/hash_set" -#include "Support/EquivalenceClasses.h" -class BUDataStructures; -class TDDataStructures; -class DSNode; -class DSGraph; -class CallInst; - -namespace PA { - /// FuncInfo - Represent the pool allocation information for one function in - /// the program. Note that many functions must actually be cloned in order - /// for pool allocation to add arguments to the function signature. In this - /// case, the Clone and NewToOldValueMap information identify how the clone - /// maps to the original function... - /// - struct FuncInfo { - /// MarkedNodes - The set of nodes which are not locally pool allocatable in - /// the current function. - /// - hash_set<DSNode*> MarkedNodes; - - /// Clone - The cloned version of the function, if applicable. - Function *Clone; - - /// ArgNodes - The list of DSNodes which have pools passed in as arguments. - /// - std::vector<DSNode*> ArgNodes; - - /// In order to handle indirect functions, the start and end of the - /// arguments that are useful to this function. - /// The pool arguments useful to this function are PoolArgFirst to - /// PoolArgLast not inclusive. - int PoolArgFirst, PoolArgLast; - - /// PoolDescriptors - The Value* (either an argument or an alloca) which - /// defines the pool descriptor for this DSNode. Pools are mapped one to - /// one with nodes in the DSGraph, so this contains a pointer to the node it - /// corresponds to. In addition, the pool is initialized by calling the - /// "poolinit" library function with a chunk of memory allocated with an - /// alloca instruction. This entry contains a pointer to that alloca if the - /// pool is locally allocated or the argument it is passed in through if - /// not. - /// Note: Does not include pool arguments that are passed in because of - /// indirect function calls that are not used in the function. - std::map<DSNode*, Value*> PoolDescriptors; - - /// NewToOldValueMap - When and if a function needs to be cloned, this map - /// contains a mapping from all of the values in the new function back to - /// the values they correspond to in the old function. - /// - std::map<Value*, const Value*> NewToOldValueMap; - }; -} - -/// PoolAllocate - The main pool allocation pass -/// -class PoolAllocate : public Pass { - Module *CurModule; - BUDataStructures *BU; - - TDDataStructures *TDDS; - - hash_set<Function*> InlinedFuncs; - - std::map<Function*, PA::FuncInfo> FunctionInfo; - - void buildIndirectFunctionSets(Module &M); - - void FindFunctionPoolArgs(Function &F); - - // Debug function to print the FuncECs - void printFuncECs(); - - public: - Function *PoolInit, *PoolDestroy, *PoolAlloc, *PoolAllocArray, *PoolFree; - - // Equivalence class where functions that can potentially be called via - // the same function pointer are in the same class. - EquivalenceClasses<Function *> FuncECs; - - // Map from an Indirect CallInst to the set of Functions that it can point to - std::multimap<CallInst *, Function *> CallInstTargets; - - // This maps an equivalence class to the last pool argument number for that - // class. This is used because the pool arguments for all functions within - // an equivalence class is passed to all the functions in that class. - // If an equivalence class does not require pool arguments, it is not - // on this map. - std::map<Function *, int> EqClass2LastPoolArg; - - // Exception flags - // CollapseFlag set if all data structures are not pool allocated, due to - // collapsing of nodes in the DS graph - unsigned CollapseFlag; - - public: - bool run(Module &M); - - virtual void getAnalysisUsage(AnalysisUsage &AU) const; - - BUDataStructures &getBUDataStructures() const { return *BU; } - - PA::FuncInfo *getFuncInfo(Function &F) { - std::map<Function*, PA::FuncInfo>::iterator I = FunctionInfo.find(&F); - return I != FunctionInfo.end() ? &I->second : 0; - } - - Module *getCurModule() { return CurModule; } - - private: - - /// AddPoolPrototypes - Add prototypes for the pool functions to the - /// specified module and update the Pool* instance variables to point to - /// them. - /// - void AddPoolPrototypes(); - - /// 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 *MakeFunctionClone(Function &F); - - /// ProcessFunctionBody - Rewrite the body of a transformed function to use - /// pool allocation where appropriate. - /// - void ProcessFunctionBody(Function &Old, Function &New); - - /// 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 CreatePools(Function &F, const std::vector<DSNode*> &NodesToPA, - std::map<DSNode*, Value*> &PoolDescriptors); - - void TransformFunctionBody(Function &F, Function &OldF, - DSGraph &G, PA::FuncInfo &FI); - - void InlineIndirectCalls(Function &F, DSGraph &G, - hash_set<Function*> &visited); -}; - -#endif
diff --git a/poolalloc/lib/DSA/BottomUpClosure.cpp b/poolalloc/lib/DSA/BottomUpClosure.cpp deleted file mode 100644 index dd141f2..0000000 --- a/poolalloc/lib/DSA/BottomUpClosure.cpp +++ /dev/null
@@ -1,302 +0,0 @@ -//===- BottomUpClosure.cpp - Compute bottom-up interprocedural closure ----===// -// -// This file implements the BUDataStructures class, which represents the -// Bottom-Up Interprocedural closure of the data structure graph over the -// program. This is useful for applications like pool allocation, but **not** -// applications like alias analysis. -// -//===----------------------------------------------------------------------===// - -#include "llvm/Analysis/DataStructure.h" -#include "llvm/Module.h" -#include "Support/Statistic.h" -#include "Support/Debug.h" -#include "DSCallSiteIterator.h" - -namespace { - Statistic<> MaxSCC("budatastructure", "Maximum SCC Size in Call Graph"); - Statistic<> NumBUInlines("budatastructures", "Number of graphs inlined"); - Statistic<> NumCallEdges("budatastructures", "Number of 'actual' call edges"); - - RegisterAnalysis<BUDataStructures> - X("budatastructure", "Bottom-up Data Structure Analysis"); -} - -using namespace DS; - -// run - Calculate the bottom up data structure graphs for each function in the -// program. -// -bool BUDataStructures::run(Module &M) { - LocalDataStructures &LocalDSA = getAnalysis<LocalDataStructures>(); - GlobalsGraph = new DSGraph(LocalDSA.getGlobalsGraph()); - GlobalsGraph->setPrintAuxCalls(); - - Function *MainFunc = M.getMainFunction(); - if (MainFunc) - calculateReachableGraphs(MainFunc); - - // Calculate the graphs for any functions that are unreachable from main... - for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) - if (!I->isExternal() && !DSInfo.count(I)) { -#ifndef NDEBUG - if (MainFunc) - std::cerr << "*** Function unreachable from main: " - << I->getName() << "\n"; -#endif - calculateReachableGraphs(I); // Calculate all graphs... - } - - NumCallEdges += ActualCallees.size(); - return false; -} - -void BUDataStructures::calculateReachableGraphs(Function *F) { - std::vector<Function*> Stack; - hash_map<Function*, unsigned> ValMap; - unsigned NextID = 1; - calculateGraphs(F, Stack, NextID, ValMap); -} - -DSGraph &BUDataStructures::getOrCreateGraph(Function *F) { - // Has the graph already been created? - DSGraph *&Graph = DSInfo[F]; - if (Graph) return *Graph; - - // Copy the local version into DSInfo... - Graph = new DSGraph(getAnalysis<LocalDataStructures>().getDSGraph(*F)); - - Graph->setGlobalsGraph(GlobalsGraph); - Graph->setPrintAuxCalls(); - - // Start with a copy of the original call sites... - Graph->getAuxFunctionCalls() = Graph->getFunctionCalls(); - return *Graph; -} - -unsigned BUDataStructures::calculateGraphs(Function *F, - std::vector<Function*> &Stack, - unsigned &NextID, - hash_map<Function*, unsigned> &ValMap) { - assert(ValMap.find(F) == ValMap.end() && "Shouldn't revisit functions!"); - unsigned Min = NextID++, MyID = Min; - ValMap[F] = Min; - Stack.push_back(F); - - if (F->isExternal()) { // sprintf, fprintf, sscanf, etc... - // No callees! - Stack.pop_back(); - ValMap[F] = ~0; - return Min; - } - - DSGraph &Graph = getOrCreateGraph(F); - - // The edges out of the current node are the call site targets... - for (DSCallSiteIterator I = DSCallSiteIterator::begin_aux(Graph), - E = DSCallSiteIterator::end_aux(Graph); I != E; ++I) { - Function *Callee = *I; - unsigned M; - // Have we visited the destination function yet? - hash_map<Function*, unsigned>::iterator It = ValMap.find(Callee); - if (It == ValMap.end()) // No, visit it now. - M = calculateGraphs(Callee, Stack, NextID, ValMap); - else // Yes, get it's number. - M = It->second; - if (M < Min) Min = M; - } - - assert(ValMap[F] == MyID && "SCC construction assumption wrong!"); - if (Min != MyID) - return Min; // This is part of a larger SCC! - - // If this is a new SCC, process it now. - if (Stack.back() == F) { // Special case the single "SCC" case here. - DEBUG(std::cerr << "Visiting single node SCC #: " << MyID << " fn: " - << F->getName() << "\n"); - Stack.pop_back(); - DSGraph &G = getDSGraph(*F); - DEBUG(std::cerr << " [BU] Calculating graph for: " << F->getName()<< "\n"); - calculateGraph(G); - DEBUG(std::cerr << " [BU] Done inlining: " << F->getName() << " [" - << G.getGraphSize() << "+" << G.getAuxFunctionCalls().size() - << "]\n"); - - if (MaxSCC < 1) MaxSCC = 1; - - // Should we revisit the graph? - if (DSCallSiteIterator::begin_aux(G) != DSCallSiteIterator::end_aux(G)) { - ValMap.erase(F); - return calculateGraphs(F, Stack, NextID, ValMap); - } else { - ValMap[F] = ~0U; - } - return MyID; - - } else { - // SCCFunctions - Keep track of the functions in the current SCC - // - hash_set<Function*> SCCFunctions; - - Function *NF; - std::vector<Function*>::iterator FirstInSCC = Stack.end(); - DSGraph *SCCGraph = 0; - do { - NF = *--FirstInSCC; - ValMap[NF] = ~0U; - SCCFunctions.insert(NF); - - // Figure out which graph is the largest one, in order to speed things up - // a bit in situations where functions in the SCC have widely different - // graph sizes. - DSGraph &NFGraph = getDSGraph(*NF); - if (!SCCGraph || SCCGraph->getGraphSize() < NFGraph.getGraphSize()) - SCCGraph = &NFGraph; - } while (NF != F); - - std::cerr << "Calculating graph for SCC #: " << MyID << " of size: " - << SCCFunctions.size() << "\n"; - - // Compute the Max SCC Size... - if (MaxSCC < SCCFunctions.size()) - MaxSCC = SCCFunctions.size(); - - // First thing first, collapse all of the DSGraphs into a single graph for - // the entire SCC. We computed the largest graph, so clone all of the other - // (smaller) graphs into it. Discard all of the old graphs. - // - for (hash_set<Function*>::iterator I = SCCFunctions.begin(), - E = SCCFunctions.end(); I != E; ++I) { - DSGraph &G = getDSGraph(**I); - if (&G != SCCGraph) { - DSGraph::NodeMapTy NodeMap; - SCCGraph->cloneInto(G, SCCGraph->getScalarMap(), - SCCGraph->getReturnNodes(), NodeMap, 0); - // Update the DSInfo map and delete the old graph... - DSInfo[*I] = SCCGraph; - delete &G; - } - } - - // Clean up the graph before we start inlining a bunch again... - SCCGraph->removeTriviallyDeadNodes(); - - // Now that we have one big happy family, resolve all of the call sites in - // the graph... - calculateGraph(*SCCGraph); - DEBUG(std::cerr << " [BU] Done inlining SCC [" << SCCGraph->getGraphSize() - << "+" << SCCGraph->getAuxFunctionCalls().size() << "]\n"); - - std::cerr << "DONE with SCC #: " << MyID << "\n"; - - // We never have to revisit "SCC" processed functions... - - // Drop the stuff we don't need from the end of the stack - Stack.erase(FirstInSCC, Stack.end()); - return MyID; - } - - return MyID; // == Min -} - - -// releaseMemory - If the pass pipeline is done with this pass, we can release -// our memory... here... -// -void BUDataStructures::releaseMemory() { - for (hash_map<Function*, DSGraph*>::iterator I = DSInfo.begin(), - E = DSInfo.end(); I != E; ++I) { - I->second->getReturnNodes().erase(I->first); - if (I->second->getReturnNodes().empty()) - delete I->second; - } - - // Empty map so next time memory is released, data structures are not - // re-deleted. - DSInfo.clear(); - delete GlobalsGraph; - GlobalsGraph = 0; -} - -void BUDataStructures::calculateGraph(DSGraph &Graph) { - // Move our call site list into TempFCs so that inline call sites go into the - // new call site list and doesn't invalidate our iterators! - std::vector<DSCallSite> TempFCs; - std::vector<DSCallSite> &AuxCallsList = Graph.getAuxFunctionCalls(); - TempFCs.swap(AuxCallsList); - - DSGraph::ReturnNodesTy &ReturnNodes = Graph.getReturnNodes(); - - // Loop over all of the resolvable call sites - unsigned LastCallSiteIdx = ~0U; - for (DSCallSiteIterator I = DSCallSiteIterator::begin(TempFCs), - E = DSCallSiteIterator::end(TempFCs); I != E; ++I) { - // If we skipped over any call sites, they must be unresolvable, copy them - // to the real call site list. - LastCallSiteIdx++; - for (; LastCallSiteIdx < I.getCallSiteIdx(); ++LastCallSiteIdx) - AuxCallsList.push_back(TempFCs[LastCallSiteIdx]); - LastCallSiteIdx = I.getCallSiteIdx(); - - // Resolve the current call... - Function *Callee = *I; - DSCallSite CS = I.getCallSite(); - - if (Callee->isExternal()) { - // Ignore this case, simple varargs functions we cannot stub out! - } else if (ReturnNodes.find(Callee) != ReturnNodes.end()) { - // Self recursion... simply link up the formal arguments with the - // actual arguments... - DEBUG(std::cerr << " Self Inlining: " << Callee->getName() << "\n"); - - // Handle self recursion by resolving the arguments and return value - Graph.mergeInGraph(CS, *Callee, Graph, 0); - - } else { - ActualCallees.insert(std::make_pair(&CS.getCallInst(), Callee)); - - // Get the data structure graph for the called function. - // - DSGraph &GI = getDSGraph(*Callee); // Graph to inline - - DEBUG(std::cerr << " Inlining graph for " << Callee->getName() - << "[" << GI.getGraphSize() << "+" - << GI.getAuxFunctionCalls().size() << "] into '" - << Graph.getFunctionNames() << "' [" << Graph.getGraphSize() << "+" - << Graph.getAuxFunctionCalls().size() << "]\n"); - - // Handle self recursion by resolving the arguments and return value - Graph.mergeInGraph(CS, *Callee, GI, - DSGraph::KeepModRefBits | - DSGraph::StripAllocaBit | DSGraph::DontCloneCallNodes); - ++NumBUInlines; - -#if 0 - Graph.writeGraphToFile(std::cerr, "bu_" + F.getName() + "_after_" + - Callee->getName()); -#endif - } - } - - // Make sure to catch any leftover unresolvable calls... - for (++LastCallSiteIdx; LastCallSiteIdx < TempFCs.size(); ++LastCallSiteIdx) - AuxCallsList.push_back(TempFCs[LastCallSiteIdx]); - - TempFCs.clear(); - - // Re-materialize nodes from the globals graph. - // Do not ignore globals inlined from callees -- they are not up-to-date! - Graph.getInlinedGlobals().clear(); - Graph.updateFromGlobalGraph(); - - // Recompute the Incomplete markers - Graph.maskIncompleteMarkers(); - Graph.markIncompleteNodes(DSGraph::MarkFormalArgs); - - // Delete dead nodes. Treat globals that are unreachable but that can - // reach live nodes as live. - Graph.removeDeadNodes(DSGraph::KeepUnreachableGlobals); - - //Graph.writeGraphToFile(std::cerr, "bu_" + F.getName()); -} -
diff --git a/poolalloc/lib/DSA/DSCallSiteIterator.h b/poolalloc/lib/DSA/DSCallSiteIterator.h deleted file mode 100644 index acbf808..0000000 --- a/poolalloc/lib/DSA/DSCallSiteIterator.h +++ /dev/null
@@ -1,126 +0,0 @@ -//===- DSCallSiteIterator.h - Iterator for DSGraph call sites ---*- C++ -*-===// -// -// This file implements an iterator for complete call sites in DSGraphs. This -// code can either iterator over the normal call list or the aux calls list, and -// is used by the TD and BU passes. -// -//===----------------------------------------------------------------------===// - -#ifndef DSCALLSITEITERATOR_H -#define DSCALLSITEITERATOR_H - -#include "llvm/Analysis/DSGraph.h" -#include "llvm/Function.h" - -struct DSCallSiteIterator { - // FCs are the edges out of the current node are the call site targets... - const std::vector<DSCallSite> *FCs; - unsigned CallSite; - unsigned CallSiteEntry; - - DSCallSiteIterator(const std::vector<DSCallSite> &CS) : FCs(&CS) { - CallSite = 0; CallSiteEntry = 0; - advanceToValidCallee(); - } - - // End iterator ctor... - DSCallSiteIterator(const std::vector<DSCallSite> &CS, bool) : FCs(&CS) { - CallSite = FCs->size(); CallSiteEntry = 0; - } - - static bool isVAHackFn(const Function *F) { - return F->getName() == "printf" || F->getName() == "sscanf" || - F->getName() == "fprintf" || F->getName() == "open" || - F->getName() == "sprintf" || F->getName() == "fputs" || - F->getName() == "fscanf" || F->getName() == "bzero" || - F->getName() == "memset"; - } - - // isUnresolvableFunction - Return true if this is an unresolvable - // external function. A direct or indirect call to this cannot be resolved. - // - static bool isUnresolvableFunc(const Function* callee) { - return callee->isExternal() && !isVAHackFn(callee); - } - - void advanceToValidCallee() { - while (CallSite < FCs->size()) { - if ((*FCs)[CallSite].isDirectCall()) { - if (CallSiteEntry == 0 && // direct call only has one target... - ! isUnresolvableFunc((*FCs)[CallSite].getCalleeFunc())) - return; // and not an unresolvable external func - } else { - DSNode *CalleeNode = (*FCs)[CallSite].getCalleeNode(); - if (CallSiteEntry || isCompleteNode(CalleeNode)) { - const std::vector<GlobalValue*> &Callees = CalleeNode->getGlobals(); - while (CallSiteEntry < Callees.size()) { - if (isa<Function>(Callees[CallSiteEntry])) - return; - ++CallSiteEntry; - } - } - } - CallSiteEntry = 0; - ++CallSite; - } - } - - // isCompleteNode - Return true if we know all of the targets of this node, - // and if the call sites are not external. - // - static inline bool isCompleteNode(DSNode *N) { - if (N->isIncomplete()) return false; - const std::vector<GlobalValue*> &Callees = N->getGlobals(); - for (unsigned i = 0, e = Callees.size(); i != e; ++i) - if (isUnresolvableFunc(cast<Function>(Callees[i]))) - return false; // Unresolvable external function found... - return true; // otherwise ok - } - -public: - static DSCallSiteIterator begin_aux(DSGraph &G) { - return G.getAuxFunctionCalls(); - } - static DSCallSiteIterator end_aux(DSGraph &G) { - return DSCallSiteIterator(G.getAuxFunctionCalls(), true); - } - static DSCallSiteIterator begin_std(DSGraph &G) { - return G.getFunctionCalls(); - } - static DSCallSiteIterator end_std(DSGraph &G) { - return DSCallSiteIterator(G.getFunctionCalls(), true); - } - static DSCallSiteIterator begin(std::vector<DSCallSite> &CSs) { return CSs; } - static DSCallSiteIterator end(std::vector<DSCallSite> &CSs) { - return DSCallSiteIterator(CSs, true); - } - bool operator==(const DSCallSiteIterator &CSI) const { - return CallSite == CSI.CallSite && CallSiteEntry == CSI.CallSiteEntry; - } - bool operator!=(const DSCallSiteIterator &CSI) const { - return !operator==(CSI); - } - - unsigned getCallSiteIdx() const { return CallSite; } - const DSCallSite &getCallSite() const { return (*FCs)[CallSite]; } - - Function *operator*() const { - if ((*FCs)[CallSite].isDirectCall()) { - return (*FCs)[CallSite].getCalleeFunc(); - } else { - DSNode *Node = (*FCs)[CallSite].getCalleeNode(); - return cast<Function>(Node->getGlobals()[CallSiteEntry]); - } - } - - DSCallSiteIterator& operator++() { // Preincrement - ++CallSiteEntry; - advanceToValidCallee(); - return *this; - } - DSCallSiteIterator operator++(int) { // Postincrement - DSCallSiteIterator tmp = *this; ++*this; return tmp; - } -}; - -#endif
diff --git a/poolalloc/lib/DSA/DataStructure.cpp b/poolalloc/lib/DSA/DataStructure.cpp deleted file mode 100644 index 241c2a9..0000000 --- a/poolalloc/lib/DSA/DataStructure.cpp +++ /dev/null
@@ -1,1612 +0,0 @@ -//===- DataStructure.cpp - Implement the core data structure analysis -----===// -// -// This file implements the core data structure functionality. -// -//===----------------------------------------------------------------------===// - -#include "llvm/Analysis/DSGraph.h" -#include "llvm/Function.h" -#include "llvm/iOther.h" -#include "llvm/DerivedTypes.h" -#include "llvm/Target/TargetData.h" -#include "llvm/Assembly/Writer.h" -#include "Support/Debug.h" -#include "Support/STLExtras.h" -#include "Support/Statistic.h" -#include "Support/Timer.h" -#include <algorithm> - -namespace { - Statistic<> NumFolds ("dsnode", "Number of nodes completely folded"); - Statistic<> NumCallNodesMerged("dsnode", "Number of call nodes merged"); -}; - -namespace DS { // TODO: FIXME - extern TargetData TD; -} -using namespace DS; - -DSNode *DSNodeHandle::HandleForwarding() const { - assert(!N->ForwardNH.isNull() && "Can only be invoked if forwarding!"); - - // Handle node forwarding here! - DSNode *Next = N->ForwardNH.getNode(); // Cause recursive shrinkage - Offset += N->ForwardNH.getOffset(); - - if (--N->NumReferrers == 0) { - // Removing the last referrer to the node, sever the forwarding link - N->stopForwarding(); - } - - N = Next; - N->NumReferrers++; - if (N->Size <= Offset) { - assert(N->Size <= 1 && "Forwarded to shrunk but not collapsed node?"); - Offset = 0; - } - return N; -} - -//===----------------------------------------------------------------------===// -// DSNode Implementation -//===----------------------------------------------------------------------===// - -DSNode::DSNode(const Type *T, DSGraph *G) - : NumReferrers(0), Size(0), ParentGraph(G), Ty(Type::VoidTy), NodeType(0) { - // Add the type entry if it is specified... - if (T) mergeTypeInfo(T, 0); - G->getNodes().push_back(this); -} - -// DSNode copy constructor... do not copy over the referrers list! -DSNode::DSNode(const DSNode &N, DSGraph *G) - : NumReferrers(0), Size(N.Size), ParentGraph(G), - Ty(N.Ty), Links(N.Links), Globals(N.Globals), NodeType(N.NodeType) { - G->getNodes().push_back(this); -} - -void DSNode::assertOK() const { - assert((Ty != Type::VoidTy || - Ty == Type::VoidTy && (Size == 0 || - (NodeType & DSNode::Array))) && - "Node not OK!"); - - assert(ParentGraph && "Node has no parent?"); - const DSGraph::ScalarMapTy &SM = ParentGraph->getScalarMap(); - for (unsigned i = 0, e = Globals.size(); i != e; ++i) { - assert(SM.find(Globals[i]) != SM.end()); - assert(SM.find(Globals[i])->second.getNode() == this); - } -} - -/// forwardNode - Mark this node as being obsolete, and all references to it -/// should be forwarded to the specified node and offset. -/// -void DSNode::forwardNode(DSNode *To, unsigned Offset) { - assert(this != To && "Cannot forward a node to itself!"); - assert(ForwardNH.isNull() && "Already forwarding from this node!"); - if (To->Size <= 1) Offset = 0; - assert((Offset < To->Size || (Offset == To->Size && Offset == 0)) && - "Forwarded offset is wrong!"); - ForwardNH.setNode(To); - ForwardNH.setOffset(Offset); - NodeType = DEAD; - Size = 0; - Ty = Type::VoidTy; -} - -// addGlobal - Add an entry for a global value to the Globals list. This also -// marks the node with the 'G' flag if it does not already have it. -// -void DSNode::addGlobal(GlobalValue *GV) { - // Keep the list sorted. - std::vector<GlobalValue*>::iterator I = - std::lower_bound(Globals.begin(), Globals.end(), GV); - - if (I == Globals.end() || *I != GV) { - //assert(GV->getType()->getElementType() == Ty); - Globals.insert(I, GV); - NodeType |= GlobalNode; - } -} - -/// foldNodeCompletely - If we determine that this node has some funny -/// behavior happening to it that we cannot represent, we fold it down to a -/// single, completely pessimistic, node. This node is represented as a -/// single byte with a single TypeEntry of "void". -/// -void DSNode::foldNodeCompletely() { - if (isNodeCompletelyFolded()) return; // If this node is already folded... - - ++NumFolds; - - // Create the node we are going to forward to... - DSNode *DestNode = new DSNode(0, ParentGraph); - DestNode->NodeType = NodeType|DSNode::Array; - DestNode->Ty = Type::VoidTy; - DestNode->Size = 1; - DestNode->Globals.swap(Globals); - - // Start forwarding to the destination node... - forwardNode(DestNode, 0); - - if (Links.size()) { - DestNode->Links.push_back(Links[0]); - DSNodeHandle NH(DestNode); - - // If we have links, merge all of our outgoing links together... - for (unsigned i = Links.size()-1; i != 0; --i) - NH.getNode()->Links[0].mergeWith(Links[i]); - Links.clear(); - } else { - DestNode->Links.resize(1); - } -} - -/// isNodeCompletelyFolded - Return true if this node has been completely -/// folded down to something that can never be expanded, effectively losing -/// all of the field sensitivity that may be present in the node. -/// -bool DSNode::isNodeCompletelyFolded() const { - return getSize() == 1 && Ty == Type::VoidTy && isArray(); -} - - -namespace { - /// TypeElementWalker Class - Used for implementation of physical subtyping... - /// - class TypeElementWalker { - struct StackState { - const Type *Ty; - unsigned Offset; - unsigned Idx; - StackState(const Type *T, unsigned Off = 0) - : Ty(T), Offset(Off), Idx(0) {} - }; - - std::vector<StackState> Stack; - public: - TypeElementWalker(const Type *T) { - Stack.push_back(T); - StepToLeaf(); - } - - bool isDone() const { return Stack.empty(); } - const Type *getCurrentType() const { return Stack.back().Ty; } - unsigned getCurrentOffset() const { return Stack.back().Offset; } - - void StepToNextType() { - PopStackAndAdvance(); - StepToLeaf(); - } - - private: - /// PopStackAndAdvance - Pop the current element off of the stack and - /// advance the underlying element to the next contained member. - void PopStackAndAdvance() { - assert(!Stack.empty() && "Cannot pop an empty stack!"); - Stack.pop_back(); - while (!Stack.empty()) { - StackState &SS = Stack.back(); - if (const StructType *ST = dyn_cast<StructType>(SS.Ty)) { - ++SS.Idx; - if (SS.Idx != ST->getElementTypes().size()) { - const StructLayout *SL = TD.getStructLayout(ST); - SS.Offset += SL->MemberOffsets[SS.Idx]-SL->MemberOffsets[SS.Idx-1]; - return; - } - Stack.pop_back(); // At the end of the structure - } else { - const ArrayType *AT = cast<ArrayType>(SS.Ty); - ++SS.Idx; - if (SS.Idx != AT->getNumElements()) { - SS.Offset += TD.getTypeSize(AT->getElementType()); - return; - } - Stack.pop_back(); // At the end of the array - } - } - } - - /// StepToLeaf - Used by physical subtyping to move to the first leaf node - /// on the type stack. - void StepToLeaf() { - if (Stack.empty()) return; - while (!Stack.empty() && !Stack.back().Ty->isFirstClassType()) { - StackState &SS = Stack.back(); - if (const StructType *ST = dyn_cast<StructType>(SS.Ty)) { - if (ST->getElementTypes().empty()) { - assert(SS.Idx == 0); - PopStackAndAdvance(); - } else { - // Step into the structure... - assert(SS.Idx < ST->getElementTypes().size()); - const StructLayout *SL = TD.getStructLayout(ST); - Stack.push_back(StackState(ST->getElementTypes()[SS.Idx], - SS.Offset+SL->MemberOffsets[SS.Idx])); - } - } else { - const ArrayType *AT = cast<ArrayType>(SS.Ty); - if (AT->getNumElements() == 0) { - assert(SS.Idx == 0); - PopStackAndAdvance(); - } else { - // Step into the array... - assert(SS.Idx < AT->getNumElements()); - Stack.push_back(StackState(AT->getElementType(), - SS.Offset+SS.Idx* - TD.getTypeSize(AT->getElementType()))); - } - } - } - } - }; -} - -/// ElementTypesAreCompatible - Check to see if the specified types are -/// "physically" compatible. If so, return true, else return false. We only -/// have to check the fields in T1: T2 may be larger than T1. -/// -static bool ElementTypesAreCompatible(const Type *T1, const Type *T2) { - TypeElementWalker T1W(T1), T2W(T2); - - while (!T1W.isDone() && !T2W.isDone()) { - if (T1W.getCurrentOffset() != T2W.getCurrentOffset()) - return false; - - const Type *T1 = T1W.getCurrentType(); - const Type *T2 = T2W.getCurrentType(); - if (T1 != T2 && !T1->isLosslesslyConvertibleTo(T2)) - return false; - - T1W.StepToNextType(); - T2W.StepToNextType(); - } - - return T1W.isDone(); -} - - -/// mergeTypeInfo - This method merges the specified type into the current node -/// at the specified offset. This may update the current node's type record if -/// this gives more information to the node, it may do nothing to the node if -/// this information is already known, or it may merge the node completely (and -/// return true) if the information is incompatible with what is already known. -/// -/// This method returns true if the node is completely folded, otherwise false. -/// -bool DSNode::mergeTypeInfo(const Type *NewTy, unsigned Offset, - bool FoldIfIncompatible) { - // Check to make sure the Size member is up-to-date. Size can be one of the - // following: - // Size = 0, Ty = Void: Nothing is known about this node. - // Size = 0, Ty = FnTy: FunctionPtr doesn't have a size, so we use zero - // Size = 1, Ty = Void, Array = 1: The node is collapsed - // Otherwise, sizeof(Ty) = Size - // - assert(((Size == 0 && Ty == Type::VoidTy && !isArray()) || - (Size == 0 && !Ty->isSized() && !isArray()) || - (Size == 1 && Ty == Type::VoidTy && isArray()) || - (Size == 0 && !Ty->isSized() && !isArray()) || - (TD.getTypeSize(Ty) == Size)) && - "Size member of DSNode doesn't match the type structure!"); - assert(NewTy != Type::VoidTy && "Cannot merge void type into DSNode!"); - - if (Offset == 0 && NewTy == Ty) - return false; // This should be a common case, handle it efficiently - - // Return true immediately if the node is completely folded. - if (isNodeCompletelyFolded()) return true; - - // If this is an array type, eliminate the outside arrays because they won't - // be used anyway. This greatly reduces the size of large static arrays used - // as global variables, for example. - // - bool WillBeArray = false; - while (const ArrayType *AT = dyn_cast<ArrayType>(NewTy)) { - // FIXME: we might want to keep small arrays, but must be careful about - // things like: [2 x [10000 x int*]] - NewTy = AT->getElementType(); - WillBeArray = true; - } - - // Figure out how big the new type we're merging in is... - unsigned NewTySize = NewTy->isSized() ? TD.getTypeSize(NewTy) : 0; - - // Otherwise check to see if we can fold this type into the current node. If - // we can't, we fold the node completely, if we can, we potentially update our - // internal state. - // - if (Ty == Type::VoidTy) { - // If this is the first type that this node has seen, just accept it without - // question.... - assert(Offset == 0 && "Cannot have an offset into a void node!"); - assert(!isArray() && "This shouldn't happen!"); - Ty = NewTy; - NodeType &= ~Array; - if (WillBeArray) NodeType |= Array; - Size = NewTySize; - - // Calculate the number of outgoing links from this node. - Links.resize((Size+DS::PointerSize-1) >> DS::PointerShift); - return false; - } - - // Handle node expansion case here... - if (Offset+NewTySize > Size) { - // It is illegal to grow this node if we have treated it as an array of - // objects... - if (isArray()) { - if (FoldIfIncompatible) foldNodeCompletely(); - return true; - } - - if (Offset) { // We could handle this case, but we don't for now... - std::cerr << "UNIMP: Trying to merge a growth type into " - << "offset != 0: Collapsing!\n"; - if (FoldIfIncompatible) foldNodeCompletely(); - return true; - } - - // Okay, the situation is nice and simple, we are trying to merge a type in - // at offset 0 that is bigger than our current type. Implement this by - // switching to the new type and then merge in the smaller one, which should - // hit the other code path here. If the other code path decides it's not - // ok, it will collapse the node as appropriate. - // - const Type *OldTy = Ty; - Ty = NewTy; - NodeType &= ~Array; - if (WillBeArray) NodeType |= Array; - Size = NewTySize; - - // Must grow links to be the appropriate size... - Links.resize((Size+DS::PointerSize-1) >> DS::PointerShift); - - // Merge in the old type now... which is guaranteed to be smaller than the - // "current" type. - return mergeTypeInfo(OldTy, 0); - } - - assert(Offset <= Size && - "Cannot merge something into a part of our type that doesn't exist!"); - - // Find the section of Ty that NewTy overlaps with... first we find the - // type that starts at offset Offset. - // - unsigned O = 0; - const Type *SubType = Ty; - while (O < Offset) { - assert(Offset-O < TD.getTypeSize(SubType) && "Offset out of range!"); - - switch (SubType->getPrimitiveID()) { - case Type::StructTyID: { - const StructType *STy = cast<StructType>(SubType); - const StructLayout &SL = *TD.getStructLayout(STy); - - unsigned i = 0, e = SL.MemberOffsets.size(); - for (; i+1 < e && SL.MemberOffsets[i+1] <= Offset-O; ++i) - /* empty */; - - // The offset we are looking for must be in the i'th element... - SubType = STy->getElementTypes()[i]; - O += SL.MemberOffsets[i]; - break; - } - case Type::ArrayTyID: { - SubType = cast<ArrayType>(SubType)->getElementType(); - unsigned ElSize = TD.getTypeSize(SubType); - unsigned Remainder = (Offset-O) % ElSize; - O = Offset-Remainder; - break; - } - default: - if (FoldIfIncompatible) foldNodeCompletely(); - return true; - } - } - - assert(O == Offset && "Could not achieve the correct offset!"); - - // If we found our type exactly, early exit - if (SubType == NewTy) return false; - - unsigned SubTypeSize = SubType->isSized() ? TD.getTypeSize(SubType) : 0; - - // Ok, we are getting desperate now. Check for physical subtyping, where we - // just require each element in the node to be compatible. - if (NewTySize <= SubTypeSize && NewTySize && NewTySize < 256 && - SubTypeSize && SubTypeSize < 256 && - ElementTypesAreCompatible(NewTy, SubType)) - return false; - - // Okay, so we found the leader type at the offset requested. Search the list - // of types that starts at this offset. If SubType is currently an array or - // structure, the type desired may actually be the first element of the - // composite type... - // - unsigned PadSize = SubTypeSize; // Size, including pad memory which is ignored - while (SubType != NewTy) { - const Type *NextSubType = 0; - unsigned NextSubTypeSize = 0; - unsigned NextPadSize = 0; - switch (SubType->getPrimitiveID()) { - case Type::StructTyID: { - const StructType *STy = cast<StructType>(SubType); - const StructLayout &SL = *TD.getStructLayout(STy); - if (SL.MemberOffsets.size() > 1) - NextPadSize = SL.MemberOffsets[1]; - else - NextPadSize = SubTypeSize; - NextSubType = STy->getElementTypes()[0]; - NextSubTypeSize = TD.getTypeSize(NextSubType); - break; - } - case Type::ArrayTyID: - NextSubType = cast<ArrayType>(SubType)->getElementType(); - NextSubTypeSize = TD.getTypeSize(NextSubType); - NextPadSize = NextSubTypeSize; - break; - default: ; - // fall out - } - - if (NextSubType == 0) - break; // In the default case, break out of the loop - - if (NextPadSize < NewTySize) - break; // Don't allow shrinking to a smaller type than NewTySize - SubType = NextSubType; - SubTypeSize = NextSubTypeSize; - PadSize = NextPadSize; - } - - // If we found the type exactly, return it... - if (SubType == NewTy) - return false; - - // Check to see if we have a compatible, but different type... - if (NewTySize == SubTypeSize) { - // Check to see if this type is obviously convertible... int -> uint f.e. - if (NewTy->isLosslesslyConvertibleTo(SubType)) - return false; - - // Check to see if we have a pointer & integer mismatch going on here, - // loading a pointer as a long, for example. - // - if (SubType->isInteger() && isa<PointerType>(NewTy) || - NewTy->isInteger() && isa<PointerType>(SubType)) - return false; - } else if (NewTySize > SubTypeSize && NewTySize <= PadSize) { - // We are accessing the field, plus some structure padding. Ignore the - // structure padding. - return false; - } - - Module *M = 0; - if (getParentGraph()->getReturnNodes().size()) - M = getParentGraph()->getReturnNodes().begin()->first->getParent(); - DEBUG(std::cerr << "MergeTypeInfo Folding OrigTy: "; - WriteTypeSymbolic(std::cerr, Ty, M) << "\n due to:"; - WriteTypeSymbolic(std::cerr, NewTy, M) << " @ " << Offset << "!\n" - << "SubType: "; - WriteTypeSymbolic(std::cerr, SubType, M) << "\n\n"); - - if (FoldIfIncompatible) foldNodeCompletely(); - return true; -} - - - -// addEdgeTo - Add an edge from the current node to the specified node. This -// can cause merging of nodes in the graph. -// -void DSNode::addEdgeTo(unsigned Offset, const DSNodeHandle &NH) { - if (NH.getNode() == 0) return; // Nothing to do - - DSNodeHandle &ExistingEdge = getLink(Offset); - if (ExistingEdge.getNode()) { - // Merge the two nodes... - ExistingEdge.mergeWith(NH); - } else { // No merging to perform... - setLink(Offset, NH); // Just force a link in there... - } -} - - -// MergeSortedVectors - Efficiently merge a vector into another vector where -// duplicates are not allowed and both are sorted. This assumes that 'T's are -// efficiently copyable and have sane comparison semantics. -// -static void MergeSortedVectors(std::vector<GlobalValue*> &Dest, - const std::vector<GlobalValue*> &Src) { - // By far, the most common cases will be the simple ones. In these cases, - // avoid having to allocate a temporary vector... - // - if (Src.empty()) { // Nothing to merge in... - return; - } else if (Dest.empty()) { // Just copy the result in... - Dest = Src; - } else if (Src.size() == 1) { // Insert a single element... - const GlobalValue *V = Src[0]; - std::vector<GlobalValue*>::iterator I = - std::lower_bound(Dest.begin(), Dest.end(), V); - if (I == Dest.end() || *I != Src[0]) // If not already contained... - Dest.insert(I, Src[0]); - } else if (Dest.size() == 1) { - GlobalValue *Tmp = Dest[0]; // Save value in temporary... - Dest = Src; // Copy over list... - std::vector<GlobalValue*>::iterator I = - std::lower_bound(Dest.begin(), Dest.end(), Tmp); - if (I == Dest.end() || *I != Tmp) // If not already contained... - Dest.insert(I, Tmp); - - } else { - // Make a copy to the side of Dest... - std::vector<GlobalValue*> Old(Dest); - - // Make space for all of the type entries now... - Dest.resize(Dest.size()+Src.size()); - - // Merge the two sorted ranges together... into Dest. - std::merge(Old.begin(), Old.end(), Src.begin(), Src.end(), Dest.begin()); - - // Now erase any duplicate entries that may have accumulated into the - // vectors (because they were in both of the input sets) - Dest.erase(std::unique(Dest.begin(), Dest.end()), Dest.end()); - } -} - - -// MergeNodes() - Helper function for DSNode::mergeWith(). -// This function does the hard work of merging two nodes, CurNodeH -// and NH after filtering out trivial cases and making sure that -// CurNodeH.offset >= NH.offset. -// -// ***WARNING*** -// Since merging may cause either node to go away, we must always -// use the node-handles to refer to the nodes. These node handles are -// automatically updated during merging, so will always provide access -// to the correct node after a merge. -// -void DSNode::MergeNodes(DSNodeHandle& CurNodeH, DSNodeHandle& NH) { - assert(CurNodeH.getOffset() >= NH.getOffset() && - "This should have been enforced in the caller."); - - // Now we know that Offset >= NH.Offset, so convert it so our "Offset" (with - // respect to NH.Offset) is now zero. NOffset is the distance from the base - // of our object that N starts from. - // - unsigned NOffset = CurNodeH.getOffset()-NH.getOffset(); - unsigned NSize = NH.getNode()->getSize(); - - // If the two nodes are of different size, and the smaller node has the array - // bit set, collapse! - if (NSize != CurNodeH.getNode()->getSize()) { - if (NSize < CurNodeH.getNode()->getSize()) { - if (NH.getNode()->isArray()) - NH.getNode()->foldNodeCompletely(); - } else if (CurNodeH.getNode()->isArray()) { - NH.getNode()->foldNodeCompletely(); - } - } - - // Merge the type entries of the two nodes together... - if (NH.getNode()->Ty != Type::VoidTy) - CurNodeH.getNode()->mergeTypeInfo(NH.getNode()->Ty, NOffset); - assert(!CurNodeH.getNode()->isDeadNode()); - - // If we are merging a node with a completely folded node, then both nodes are - // now completely folded. - // - if (CurNodeH.getNode()->isNodeCompletelyFolded()) { - if (!NH.getNode()->isNodeCompletelyFolded()) { - NH.getNode()->foldNodeCompletely(); - assert(NH.getNode() && NH.getOffset() == 0 && - "folding did not make offset 0?"); - NOffset = NH.getOffset(); - NSize = NH.getNode()->getSize(); - assert(NOffset == 0 && NSize == 1); - } - } else if (NH.getNode()->isNodeCompletelyFolded()) { - CurNodeH.getNode()->foldNodeCompletely(); - assert(CurNodeH.getNode() && CurNodeH.getOffset() == 0 && - "folding did not make offset 0?"); - NOffset = NH.getOffset(); - NSize = NH.getNode()->getSize(); - assert(NOffset == 0 && NSize == 1); - } - - DSNode *N = NH.getNode(); - if (CurNodeH.getNode() == N || N == 0) return; - assert(!CurNodeH.getNode()->isDeadNode()); - - // Merge the NodeType information... - CurNodeH.getNode()->NodeType |= N->NodeType; - - // Start forwarding to the new node! - N->forwardNode(CurNodeH.getNode(), NOffset); - assert(!CurNodeH.getNode()->isDeadNode()); - - // Make all of the outgoing links of N now be outgoing links of CurNodeH. - // - for (unsigned i = 0; i < N->getNumLinks(); ++i) { - DSNodeHandle &Link = N->getLink(i << DS::PointerShift); - if (Link.getNode()) { - // Compute the offset into the current node at which to - // merge this link. In the common case, this is a linear - // relation to the offset in the original node (with - // wrapping), but if the current node gets collapsed due to - // recursive merging, we must make sure to merge in all remaining - // links at offset zero. - unsigned MergeOffset = 0; - DSNode *CN = CurNodeH.getNode(); - if (CN->Size != 1) - MergeOffset = ((i << DS::PointerShift)+NOffset) % CN->getSize(); - CN->addEdgeTo(MergeOffset, Link); - } - } - - // Now that there are no outgoing edges, all of the Links are dead. - N->Links.clear(); - - // Merge the globals list... - if (!N->Globals.empty()) { - MergeSortedVectors(CurNodeH.getNode()->Globals, N->Globals); - - // Delete the globals from the old node... - std::vector<GlobalValue*>().swap(N->Globals); - } -} - - -// mergeWith - Merge this node and the specified node, moving all links to and -// from the argument node into the current node, deleting the node argument. -// Offset indicates what offset the specified node is to be merged into the -// current node. -// -// The specified node may be a null pointer (in which case, nothing happens). -// -void DSNode::mergeWith(const DSNodeHandle &NH, unsigned Offset) { - DSNode *N = NH.getNode(); - if (N == 0 || (N == this && NH.getOffset() == Offset)) - return; // Noop - - assert(!N->isDeadNode() && !isDeadNode()); - assert(!hasNoReferrers() && "Should not try to fold a useless node!"); - - if (N == this) { - // We cannot merge two pieces of the same node together, collapse the node - // completely. - DEBUG(std::cerr << "Attempting to merge two chunks of" - << " the same node together!\n"); - foldNodeCompletely(); - return; - } - - // If both nodes are not at offset 0, make sure that we are merging the node - // at an later offset into the node with the zero offset. - // - if (Offset < NH.getOffset()) { - N->mergeWith(DSNodeHandle(this, Offset), NH.getOffset()); - return; - } else if (Offset == NH.getOffset() && getSize() < N->getSize()) { - // If the offsets are the same, merge the smaller node into the bigger node - N->mergeWith(DSNodeHandle(this, Offset), NH.getOffset()); - return; - } - - // Ok, now we can merge the two nodes. Use a static helper that works with - // two node handles, since "this" may get merged away at intermediate steps. - DSNodeHandle CurNodeH(this, Offset); - DSNodeHandle NHCopy(NH); - DSNode::MergeNodes(CurNodeH, NHCopy); -} - -//===----------------------------------------------------------------------===// -// DSCallSite Implementation -//===----------------------------------------------------------------------===// - -// Define here to avoid including iOther.h and BasicBlock.h in DSGraph.h -Function &DSCallSite::getCaller() const { - return *Inst->getParent()->getParent(); -} - - -//===----------------------------------------------------------------------===// -// DSGraph Implementation -//===----------------------------------------------------------------------===// - -/// getFunctionNames - Return a space separated list of the name of the -/// functions in this graph (if any) -std::string DSGraph::getFunctionNames() const { - switch (getReturnNodes().size()) { - case 0: return "Globals graph"; - case 1: return getReturnNodes().begin()->first->getName(); - default: - std::string Return; - for (DSGraph::ReturnNodesTy::const_iterator I = getReturnNodes().begin(); - I != getReturnNodes().end(); ++I) - Return += I->first->getName() + " "; - Return.erase(Return.end()-1, Return.end()); // Remove last space character - return Return; - } -} - - -DSGraph::DSGraph(const DSGraph &G) : GlobalsGraph(0) { - PrintAuxCalls = false; - NodeMapTy NodeMap; - cloneInto(G, ScalarMap, ReturnNodes, NodeMap); - InlinedGlobals.clear(); // clear set of "up-to-date" globals -} - -DSGraph::DSGraph(const DSGraph &G, NodeMapTy &NodeMap) - : GlobalsGraph(0) { - PrintAuxCalls = false; - cloneInto(G, ScalarMap, ReturnNodes, NodeMap); - InlinedGlobals.clear(); // clear set of "up-to-date" globals -} - -DSGraph::~DSGraph() { - FunctionCalls.clear(); - AuxFunctionCalls.clear(); - InlinedGlobals.clear(); - ScalarMap.clear(); - ReturnNodes.clear(); - - // Drop all intra-node references, so that assertions don't fail... - std::for_each(Nodes.begin(), Nodes.end(), - std::mem_fun(&DSNode::dropAllReferences)); - - // Delete all of the nodes themselves... - std::for_each(Nodes.begin(), Nodes.end(), deleter<DSNode>); -} - -// dump - Allow inspection of graph in a debugger. -void DSGraph::dump() const { print(std::cerr); } - - -/// remapLinks - Change all of the Links in the current node according to the -/// specified mapping. -/// -void DSNode::remapLinks(DSGraph::NodeMapTy &OldNodeMap) { - for (unsigned i = 0, e = Links.size(); i != e; ++i) { - DSNodeHandle &H = OldNodeMap[Links[i].getNode()]; - Links[i].setNode(H.getNode()); - Links[i].setOffset(Links[i].getOffset()+H.getOffset()); - } -} - - -/// cloneReachableNodes - Clone all reachable nodes from *Node into the -/// current graph. This is a recursive function. The map OldNodeMap is a -/// map from the original nodes to their clones. -/// -void DSGraph::cloneReachableNodes(const DSNode* Node, - unsigned BitsToClear, - NodeMapTy& OldNodeMap, - NodeMapTy& CompletedNodeMap) { - if (CompletedNodeMap.find(Node) != CompletedNodeMap.end()) - return; - - DSNodeHandle& NH = OldNodeMap[Node]; - if (NH.getNode() != NULL) - return; - - // else Node has not yet been cloned: clone it and clear the specified bits - NH = new DSNode(*Node, this); // enters in OldNodeMap - NH.getNode()->maskNodeTypes(~BitsToClear); - - // now recursively clone nodes pointed to by this node - for (unsigned i = 0, e = Node->getNumLinks(); i != e; ++i) { - const DSNodeHandle &Link = Node->getLink(i << DS::PointerShift); - if (const DSNode* nextNode = Link.getNode()) - cloneReachableNodes(nextNode, BitsToClear, OldNodeMap, CompletedNodeMap); - } -} - -void DSGraph::cloneReachableSubgraph(const DSGraph& G, - const hash_set<const DSNode*>& RootNodes, - NodeMapTy& OldNodeMap, - NodeMapTy& CompletedNodeMap, - unsigned CloneFlags) { - if (RootNodes.empty()) - return; - - assert(OldNodeMap.empty() && "Returned OldNodeMap should be empty!"); - assert(&G != this && "Cannot clone graph into itself!"); - assert((*RootNodes.begin())->getParentGraph() == &G && - "Root nodes do not belong to this graph!"); - - // Remove alloca or mod/ref bits as specified... - unsigned BitsToClear = ((CloneFlags & StripAllocaBit)? DSNode::AllocaNode : 0) - | ((CloneFlags & StripModRefBits)? (DSNode::Modified | DSNode::Read) : 0) - | ((CloneFlags & StripIncompleteBit)? DSNode::Incomplete : 0); - BitsToClear |= DSNode::DEAD; // Clear dead flag... - - // Clone all nodes reachable from each root node, using a recursive helper - for (hash_set<const DSNode*>::const_iterator I = RootNodes.begin(), - E = RootNodes.end(); I != E; ++I) - cloneReachableNodes(*I, BitsToClear, OldNodeMap, CompletedNodeMap); - - // Merge the map entries in OldNodeMap and CompletedNodeMap to remap links - NodeMapTy MergedMap(OldNodeMap); - MergedMap.insert(CompletedNodeMap.begin(), CompletedNodeMap.end()); - - // Rewrite the links in the newly created nodes (the nodes in OldNodeMap) - // to point into the current graph. MergedMap gives the full mapping. - for (NodeMapTy::iterator I=OldNodeMap.begin(), E=OldNodeMap.end(); I!= E; ++I) - I->second.getNode()->remapLinks(MergedMap); - - // Now merge cloned global nodes with their copies in the current graph - // Just look through OldNodeMap to find such nodes! - for (NodeMapTy::iterator I=OldNodeMap.begin(), E=OldNodeMap.end(); I!= E; ++I) - if (I->first->isGlobalNode()) { - DSNodeHandle &GClone = I->second; - assert(GClone.getNode() != NULL && "NULL node in OldNodeMap?"); - const std::vector<GlobalValue*> &Globals = I->first->getGlobals(); - for (unsigned gi = 0, ge = Globals.size(); gi != ge; ++gi) { - DSNodeHandle &GH = ScalarMap[Globals[gi]]; - GH.mergeWith(GClone); - } - } -} - - -/// updateFromGlobalGraph - This function rematerializes global nodes and -/// nodes reachable from them from the globals graph into the current graph. -/// It invokes cloneReachableSubgraph, using the globals in the current graph -/// as the roots. It also uses the vector InlinedGlobals to avoid cloning and -/// merging globals that are already up-to-date in the current graph. In -/// practice, in the TD pass, this is likely to be a large fraction of the -/// live global nodes in each function (since most live nodes are likely to -/// have been brought up-to-date in at _some_ caller or callee). -/// -void DSGraph::updateFromGlobalGraph() { - - // Use a map to keep track of the mapping between nodes in the globals graph - // and this graph for up-to-date global nodes, which do not need to be cloned. - NodeMapTy CompletedMap; - - // Put the live, non-up-to-date global nodes into a set and the up-to-date - // ones in the map above, mapping node in GlobalsGraph to the up-to-date node. - hash_set<const DSNode*> GlobalNodeSet; - for (ScalarMapTy::const_iterator I = getScalarMap().begin(), - E = getScalarMap().end(); I != E; ++I) - if (GlobalValue* GV = dyn_cast<GlobalValue>(I->first)) { - DSNode* GNode = I->second.getNode(); - assert(GNode && "No node for live global in current Graph?"); - if (const DSNode* GGNode = GlobalsGraph->ScalarMap[GV].getNode()) - if (InlinedGlobals.count(GV) == 0) // GNode is not up-to-date - GlobalNodeSet.insert(GGNode); - else { // GNode is up-to-date - CompletedMap[GGNode] = I->second; - assert(GGNode->getNumLinks() == GNode->getNumLinks() && - "Links dont match in a node that is supposed to be up-to-date?" - "\nremapLinks() will not work if the links don't match!"); - } - } - - // Clone the subgraph reachable from the vector of nodes in GlobalNodes - // and merge the cloned global nodes with the corresponding ones, if any. - NodeMapTy OldNodeMap; - cloneReachableSubgraph(*GlobalsGraph, GlobalNodeSet, OldNodeMap,CompletedMap); - - // Merging global nodes leaves behind unused nodes: get rid of them now. - OldNodeMap.clear(); // remove references before dead node cleanup - CompletedMap.clear(); // remove references before dead node cleanup - removeTriviallyDeadNodes(); -} - -/// cloneInto - Clone the specified DSGraph into the current graph. The -/// translated ScalarMap for the old function is filled into the OldValMap -/// member, and the translated ReturnNodes map is returned into ReturnNodes. -/// -/// The CloneFlags member controls various aspects of the cloning process. -/// -void DSGraph::cloneInto(const DSGraph &G, ScalarMapTy &OldValMap, - ReturnNodesTy &OldReturnNodes, NodeMapTy &OldNodeMap, - unsigned CloneFlags) { - assert(OldNodeMap.empty() && "Returned OldNodeMap should be empty!"); - assert(&G != this && "Cannot clone graph into itself!"); - - unsigned FN = Nodes.size(); // First new node... - - // Duplicate all of the nodes, populating the node map... - Nodes.reserve(FN+G.Nodes.size()); - - // Remove alloca or mod/ref bits as specified... - unsigned BitsToClear = ((CloneFlags & StripAllocaBit)? DSNode::AllocaNode : 0) - | ((CloneFlags & StripModRefBits)? (DSNode::Modified | DSNode::Read) : 0) - | ((CloneFlags & StripIncompleteBit)? DSNode::Incomplete : 0); - BitsToClear |= DSNode::DEAD; // Clear dead flag... - for (unsigned i = 0, e = G.Nodes.size(); i != e; ++i) { - DSNode *Old = G.Nodes[i]; - DSNode *New = new DSNode(*Old, this); - New->maskNodeTypes(~BitsToClear); - OldNodeMap[Old] = New; - } - -#ifndef NDEBUG - Timer::addPeakMemoryMeasurement(); -#endif - - // Rewrite the links in the new nodes to point into the current graph now. - for (unsigned i = FN, e = Nodes.size(); i != e; ++i) - Nodes[i]->remapLinks(OldNodeMap); - - // Copy the scalar map... merging all of the global nodes... - for (ScalarMapTy::const_iterator I = G.ScalarMap.begin(), - E = G.ScalarMap.end(); I != E; ++I) { - DSNodeHandle &MappedNode = OldNodeMap[I->second.getNode()]; - DSNodeHandle &H = OldValMap[I->first]; - H.mergeWith(DSNodeHandle(MappedNode.getNode(), - I->second.getOffset()+MappedNode.getOffset())); - - // If this is a global, add the global to this fn or merge if already exists - if (GlobalValue* GV = dyn_cast<GlobalValue>(I->first)) { - ScalarMap[GV].mergeWith(H); - InlinedGlobals.insert(GV); - } - } - - if (!(CloneFlags & DontCloneCallNodes)) { - // Copy the function calls list... - unsigned FC = FunctionCalls.size(); // FirstCall - FunctionCalls.reserve(FC+G.FunctionCalls.size()); - for (unsigned i = 0, ei = G.FunctionCalls.size(); i != ei; ++i) - FunctionCalls.push_back(DSCallSite(G.FunctionCalls[i], OldNodeMap)); - } - - if (!(CloneFlags & DontCloneAuxCallNodes)) { - // Copy the auxillary function calls list... - unsigned FC = AuxFunctionCalls.size(); // FirstCall - AuxFunctionCalls.reserve(FC+G.AuxFunctionCalls.size()); - for (unsigned i = 0, ei = G.AuxFunctionCalls.size(); i != ei; ++i) - AuxFunctionCalls.push_back(DSCallSite(G.AuxFunctionCalls[i], OldNodeMap)); - } - - // Map the return node pointers over... - for (ReturnNodesTy::const_iterator I = G.getReturnNodes().begin(), - E = G.getReturnNodes().end(); I != E; ++I) { - const DSNodeHandle &Ret = I->second; - DSNodeHandle &MappedRet = OldNodeMap[Ret.getNode()]; - OldReturnNodes.insert(std::make_pair(I->first, - DSNodeHandle(MappedRet.getNode(), - MappedRet.getOffset()+Ret.getOffset()))); - } -} - -/// mergeInGraph - The method is used for merging graphs together. If the -/// argument graph is not *this, it makes a clone of the specified graph, then -/// merges the nodes specified in the call site with the formal arguments in the -/// graph. -/// -void DSGraph::mergeInGraph(const DSCallSite &CS, Function &F, - const DSGraph &Graph, unsigned CloneFlags) { - ScalarMapTy OldValMap, *ScalarMap; - DSNodeHandle RetVal; - - // If this is not a recursive call, clone the graph into this graph... - if (&Graph != this) { - // Clone the callee's graph into the current graph, keeping - // track of where scalars in the old graph _used_ to point, - // and of the new nodes matching nodes of the old graph. - NodeMapTy OldNodeMap; - - // The clone call may invalidate any of the vectors in the data - // structure graph. Strip locals and don't copy the list of callers - ReturnNodesTy OldRetNodes; - cloneInto(Graph, OldValMap, OldRetNodes, OldNodeMap, CloneFlags); - - // We need to map the arguments for the function to the cloned nodes old - // argument values. Do this now. - RetVal = OldRetNodes[&F]; - ScalarMap = &OldValMap; - } else { - RetVal = getReturnNodeFor(F); - ScalarMap = &getScalarMap(); - } - - // Merge the return value with the return value of the context... - RetVal.mergeWith(CS.getRetVal()); - - // Resolve all of the function arguments... - Function::aiterator AI = F.abegin(); - - for (unsigned i = 0, e = CS.getNumPtrArgs(); i != e; ++i, ++AI) { - // Advance the argument iterator to the first pointer argument... - while (AI != F.aend() && !isPointerType(AI->getType())) { - ++AI; -#ifndef NDEBUG - if (AI == F.aend()) - std::cerr << "Bad call to Function: " << F.getName() << "\n"; -#endif - } - if (AI == F.aend()) break; - - // Add the link from the argument scalar to the provided value - assert(ScalarMap->count(AI) && "Argument not in scalar map?"); - DSNodeHandle &NH = (*ScalarMap)[AI]; - assert(NH.getNode() && "Pointer argument without scalarmap entry?"); - NH.mergeWith(CS.getPtrArg(i)); - } -} - -/// getCallSiteForArguments - Get the arguments and return value bindings for -/// the specified function in the current graph. -/// -DSCallSite DSGraph::getCallSiteForArguments(Function &F) const { - std::vector<DSNodeHandle> Args; - - for (Function::aiterator I = F.abegin(), E = F.aend(); I != E; ++I) - if (isPointerType(I->getType())) - Args.push_back(getScalarMap().find(I)->second); - - return DSCallSite(*(CallInst*)0, getReturnNodeFor(F), &F, Args); -} - - - -// markIncompleteNodes - Mark the specified node as having contents that are not -// known with the current analysis we have performed. Because a node makes all -// of the nodes it can reach incomplete if the node itself is incomplete, we -// must recursively traverse the data structure graph, marking all reachable -// nodes as incomplete. -// -static void markIncompleteNode(DSNode *N) { - // Stop recursion if no node, or if node already marked... - if (N == 0 || N->isIncomplete()) return; - - // Actually mark the node - N->setIncompleteMarker(); - - // Recusively process children... - for (unsigned i = 0, e = N->getSize(); i < e; i += DS::PointerSize) - if (DSNode *DSN = N->getLink(i).getNode()) - markIncompleteNode(DSN); -} - -static void markIncomplete(DSCallSite &Call) { - // Then the return value is certainly incomplete! - markIncompleteNode(Call.getRetVal().getNode()); - - // All objects pointed to by function arguments are incomplete! - for (unsigned i = 0, e = Call.getNumPtrArgs(); i != e; ++i) - markIncompleteNode(Call.getPtrArg(i).getNode()); -} - -// markIncompleteNodes - Traverse the graph, identifying nodes that may be -// modified by other functions that have not been resolved yet. This marks -// nodes that are reachable through three sources of "unknownness": -// -// Global Variables, Function Calls, and Incoming Arguments -// -// For any node that may have unknown components (because something outside the -// scope of current analysis may have modified it), the 'Incomplete' flag is -// added to the NodeType. -// -void DSGraph::markIncompleteNodes(unsigned Flags) { - // Mark any incoming arguments as incomplete... - if (Flags & DSGraph::MarkFormalArgs) - for (ReturnNodesTy::iterator FI = ReturnNodes.begin(), E =ReturnNodes.end(); - FI != E; ++FI) { - Function &F = *FI->first; - if (F.getName() != "main") - for (Function::aiterator I = F.abegin(), E = F.aend(); I != E; ++I) - if (isPointerType(I->getType()) && - ScalarMap.find(I) != ScalarMap.end()) - markIncompleteNode(ScalarMap[I].getNode()); - } - - // Mark stuff passed into functions calls as being incomplete... - if (!shouldPrintAuxCalls()) - for (unsigned i = 0, e = FunctionCalls.size(); i != e; ++i) - markIncomplete(FunctionCalls[i]); - else - for (unsigned i = 0, e = AuxFunctionCalls.size(); i != e; ++i) - markIncomplete(AuxFunctionCalls[i]); - - - // Mark all global nodes as incomplete... - if ((Flags & DSGraph::IgnoreGlobals) == 0) - for (unsigned i = 0, e = Nodes.size(); i != e; ++i) - if (Nodes[i]->isGlobalNode() && Nodes[i]->getNumLinks()) - markIncompleteNode(Nodes[i]); -} - -static inline void killIfUselessEdge(DSNodeHandle &Edge) { - if (DSNode *N = Edge.getNode()) // Is there an edge? - if (N->getNumReferrers() == 1) // Does it point to a lonely node? - // No interesting info? - if ((N->getNodeFlags() & ~DSNode::Incomplete) == 0 && - N->getType() == Type::VoidTy && !N->isNodeCompletelyFolded()) - Edge.setNode(0); // Kill the edge! -} - -static inline bool nodeContainsExternalFunction(const DSNode *N) { - const std::vector<GlobalValue*> &Globals = N->getGlobals(); - for (unsigned i = 0, e = Globals.size(); i != e; ++i) - if (Globals[i]->isExternal()) - return true; - return false; -} - -static void removeIdenticalCalls(std::vector<DSCallSite> &Calls) { - - // Remove trivially identical function calls - unsigned NumFns = Calls.size(); - std::sort(Calls.begin(), Calls.end()); // Sort by callee as primary key! - - // Scan the call list cleaning it up as necessary... - DSNode *LastCalleeNode = 0; - Function *LastCalleeFunc = 0; - unsigned NumDuplicateCalls = 0; - bool LastCalleeContainsExternalFunction = false; - for (unsigned i = 0; i != Calls.size(); ++i) { - DSCallSite &CS = Calls[i]; - - // If the Callee is a useless edge, this must be an unreachable call site, - // eliminate it. - if (CS.isIndirectCall() && CS.getCalleeNode()->getNumReferrers() == 1 && - CS.getCalleeNode()->getNodeFlags() == 0) { // No useful info? - std::cerr << "WARNING: Useless call site found??\n"; - CS.swap(Calls.back()); - Calls.pop_back(); - --i; - } else { - // If the return value or any arguments point to a void node with no - // information at all in it, and the call node is the only node to point - // to it, remove the edge to the node (killing the node). - // - killIfUselessEdge(CS.getRetVal()); - for (unsigned a = 0, e = CS.getNumPtrArgs(); a != e; ++a) - killIfUselessEdge(CS.getPtrArg(a)); - - // If this call site calls the same function as the last call site, and if - // the function pointer contains an external function, this node will - // never be resolved. Merge the arguments of the call node because no - // information will be lost. - // - if ((CS.isDirectCall() && CS.getCalleeFunc() == LastCalleeFunc) || - (CS.isIndirectCall() && CS.getCalleeNode() == LastCalleeNode)) { - ++NumDuplicateCalls; - if (NumDuplicateCalls == 1) { - if (LastCalleeNode) - LastCalleeContainsExternalFunction = - nodeContainsExternalFunction(LastCalleeNode); - else - LastCalleeContainsExternalFunction = LastCalleeFunc->isExternal(); - } - -#if 1 - if (LastCalleeContainsExternalFunction || - // This should be more than enough context sensitivity! - // FIXME: Evaluate how many times this is tripped! - NumDuplicateCalls > 20) { - DSCallSite &OCS = Calls[i-1]; - OCS.mergeWith(CS); - - // The node will now be eliminated as a duplicate! - if (CS.getNumPtrArgs() < OCS.getNumPtrArgs()) - CS = OCS; - else if (CS.getNumPtrArgs() > OCS.getNumPtrArgs()) - OCS = CS; - } -#endif - } else { - if (CS.isDirectCall()) { - LastCalleeFunc = CS.getCalleeFunc(); - LastCalleeNode = 0; - } else { - LastCalleeNode = CS.getCalleeNode(); - LastCalleeFunc = 0; - } - NumDuplicateCalls = 0; - } - } - } - - Calls.erase(std::unique(Calls.begin(), Calls.end()), - Calls.end()); - - // Track the number of call nodes merged away... - NumCallNodesMerged += NumFns-Calls.size(); - - DEBUG(if (NumFns != Calls.size()) - std::cerr << "Merged " << (NumFns-Calls.size()) << " call nodes.\n";); -} - - -// removeTriviallyDeadNodes - After the graph has been constructed, this method -// removes all unreachable nodes that are created because they got merged with -// other nodes in the graph. These nodes will all be trivially unreachable, so -// we don't have to perform any non-trivial analysis here. -// -void DSGraph::removeTriviallyDeadNodes() { - removeIdenticalCalls(FunctionCalls); - removeIdenticalCalls(AuxFunctionCalls); - - bool isGlobalsGraph = !GlobalsGraph; - - for (unsigned i = 0; i != Nodes.size(); ++i) { - DSNode *Node = Nodes[i]; - - // Do not remove *any* global nodes in the globals graph. - // This is a special case because such nodes may not have I, M, R flags set. - if (Node->isGlobalNode() && isGlobalsGraph) - continue; - - if (Node->isComplete() && !Node->isModified() && !Node->isRead()) { - // This is a useless node if it has no mod/ref info (checked above), - // outgoing edges (which it cannot, as it is not modified in this - // context), and it has no incoming edges. If it is a global node it may - // have all of these properties and still have incoming edges, due to the - // scalar map, so we check those now. - // - if (Node->getNumReferrers() == Node->getGlobals().size()) { - const std::vector<GlobalValue*> &Globals = Node->getGlobals(); - - // Loop through and make sure all of the globals are referring directly - // to the node... - for (unsigned j = 0, e = Globals.size(); j != e; ++j) { - DSNode *N = ScalarMap.find(Globals[j])->second.getNode(); - assert(N == Node && "ScalarMap doesn't match globals list!"); - } - - // Make sure NumReferrers still agrees, if so, the node is truly dead. - if (Node->getNumReferrers() == Globals.size()) { - for (unsigned j = 0, e = Globals.size(); j != e; ++j) - ScalarMap.erase(Globals[j]); - Node->makeNodeDead(); - } - } - -#ifdef SANER_CODE_FOR_CHECKING_IF_ALL_REFERRERS_ARE_FROM_SCALARMAP - // - // *** It seems to me that we should be able to simply check if - // *** there are fewer or equal #referrers as #globals and make - // *** sure that all those referrers are in the scalar map? - // - if (Node->getNumReferrers() <= Node->getGlobals().size()) { - const std::vector<GlobalValue*> &Globals = Node->getGlobals(); - -#ifndef NDEBUG - // Loop through and make sure all of the globals are referring directly - // to the node... - for (unsigned j = 0, e = Globals.size(); j != e; ++j) { - DSNode *N = ScalarMap.find(Globals[j])->second.getNode(); - assert(N == Node && "ScalarMap doesn't match globals list!"); - } -#endif - - // Make sure NumReferrers still agrees. The node is truly dead. - assert(Node->getNumReferrers() == Globals.size()); - for (unsigned j = 0, e = Globals.size(); j != e; ++j) - ScalarMap.erase(Globals[j]); - Node->makeNodeDead(); - } -#endif - } - - if (Node->getNodeFlags() == 0 && Node->hasNoReferrers()) { - // This node is dead! - delete Node; // Free memory... - Nodes[i--] = Nodes.back(); - Nodes.pop_back(); // Remove from node list... - } - } -} - - -/// markReachableNodes - This method recursively traverses the specified -/// DSNodes, marking any nodes which are reachable. All reachable nodes it adds -/// to the set, which allows it to only traverse visited nodes once. -/// -void DSNode::markReachableNodes(hash_set<DSNode*> &ReachableNodes) { - if (this == 0) return; - assert(getForwardNode() == 0 && "Cannot mark a forwarded node!"); - if (ReachableNodes.count(this)) return; // Already marked reachable - ReachableNodes.insert(this); // Is reachable now - - for (unsigned i = 0, e = getSize(); i < e; i += DS::PointerSize) - getLink(i).getNode()->markReachableNodes(ReachableNodes); -} - -void DSCallSite::markReachableNodes(hash_set<DSNode*> &Nodes) { - getRetVal().getNode()->markReachableNodes(Nodes); - if (isIndirectCall()) getCalleeNode()->markReachableNodes(Nodes); - - for (unsigned i = 0, e = getNumPtrArgs(); i != e; ++i) - getPtrArg(i).getNode()->markReachableNodes(Nodes); -} - -// CanReachAliveNodes - Simple graph walker that recursively traverses the graph -// looking for a node that is marked alive. If an alive node is found, return -// true, otherwise return false. If an alive node is reachable, this node is -// marked as alive... -// -static bool CanReachAliveNodes(DSNode *N, hash_set<DSNode*> &Alive, - hash_set<DSNode*> &Visited, - bool IgnoreGlobals) { - if (N == 0) return false; - assert(N->getForwardNode() == 0 && "Cannot mark a forwarded node!"); - - // If this is a global node, it will end up in the globals graph anyway, so we - // don't need to worry about it. - if (IgnoreGlobals && N->isGlobalNode()) return false; - - // If we know that this node is alive, return so! - if (Alive.count(N)) return true; - - // Otherwise, we don't think the node is alive yet, check for infinite - // recursion. - if (Visited.count(N)) return false; // Found a cycle - Visited.insert(N); // No recursion, insert into Visited... - - for (unsigned i = 0, e = N->getSize(); i < e; i += DS::PointerSize) - if (CanReachAliveNodes(N->getLink(i).getNode(), Alive, Visited, - IgnoreGlobals)) { - N->markReachableNodes(Alive); - return true; - } - return false; -} - -// CallSiteUsesAliveArgs - Return true if the specified call site can reach any -// alive nodes. -// -static bool CallSiteUsesAliveArgs(DSCallSite &CS, hash_set<DSNode*> &Alive, - hash_set<DSNode*> &Visited, - bool IgnoreGlobals) { - if (CanReachAliveNodes(CS.getRetVal().getNode(), Alive, Visited, - IgnoreGlobals)) - return true; - if (CS.isIndirectCall() && - CanReachAliveNodes(CS.getCalleeNode(), Alive, Visited, IgnoreGlobals)) - return true; - for (unsigned i = 0, e = CS.getNumPtrArgs(); i != e; ++i) - if (CanReachAliveNodes(CS.getPtrArg(i).getNode(), Alive, Visited, - IgnoreGlobals)) - return true; - return false; -} - -// removeDeadNodes - Use a more powerful reachability analysis to eliminate -// subgraphs that are unreachable. This often occurs because the data -// structure doesn't "escape" into it's caller, and thus should be eliminated -// from the caller's graph entirely. This is only appropriate to use when -// inlining graphs. -// -void DSGraph::removeDeadNodes(unsigned Flags) { - DEBUG(AssertGraphOK(); GlobalsGraph->AssertGraphOK()); - - // Reduce the amount of work we have to do... remove dummy nodes left over by - // merging... - removeTriviallyDeadNodes(); - - // FIXME: Merge nontrivially identical call nodes... - - // Alive - a set that holds all nodes found to be reachable/alive. - hash_set<DSNode*> Alive; - std::vector<std::pair<Value*, DSNode*> > GlobalNodes; - - // Mark all nodes reachable by (non-global) scalar nodes as alive... - for (ScalarMapTy::iterator I = ScalarMap.begin(), E = ScalarMap.end(); I !=E;) - if (isa<GlobalValue>(I->first)) { // Keep track of global nodes - assert(I->second.getNode() && "Null global node?"); - assert(I->second.getNode()->isGlobalNode() && "Should be a global node!"); - GlobalNodes.push_back(std::make_pair(I->first, I->second.getNode())); - ++I; - } else { - // Check to see if this is a worthless node generated for non-pointer - // values, such as integers. Consider an addition of long types: A+B. - // Assuming we can track all uses of the value in this context, and it is - // NOT used as a pointer, we can delete the node. We will be able to - // detect this situation if the node pointed to ONLY has Unknown bit set - // in the node. In this case, the node is not incomplete, does not point - // to any other nodes (no mod/ref bits set), and is therefore - // uninteresting for data structure analysis. If we run across one of - // these, prune the scalar pointing to it. - // - DSNode *N = I->second.getNode(); - if (N->getNodeFlags() == DSNode::UnknownNode && !isa<Argument>(I->first)){ - ScalarMap.erase(I++); - } else { - I->second.getNode()->markReachableNodes(Alive); - ++I; - } - } - - // The return value is alive as well... - for (ReturnNodesTy::iterator I = ReturnNodes.begin(), E = ReturnNodes.end(); - I != E; ++I) - I->second.getNode()->markReachableNodes(Alive); - - // Mark any nodes reachable by primary calls as alive... - for (unsigned i = 0, e = FunctionCalls.size(); i != e; ++i) - FunctionCalls[i].markReachableNodes(Alive); - - // Copy and merge all information about globals to the GlobalsGraph - // if this is not a final pass (where unreachable globals are removed) - NodeMapTy GlobalNodeMap; - hash_set<const DSNode*> GlobalNodeSet; - - for (std::vector<std::pair<Value*, DSNode*> >::const_iterator - I = GlobalNodes.begin(), E = GlobalNodes.end(); I != E; ++I) - GlobalNodeSet.insert(I->second); // put global nodes into a set - - // Now find globals and aux call nodes that are already live or reach a live - // value (which makes them live in turn), and continue till no more are found. - // - bool Iterate; - hash_set<DSNode*> Visited; - std::vector<unsigned char> AuxFCallsAlive(AuxFunctionCalls.size()); - do { - Visited.clear(); - // If any global node points to a non-global that is "alive", the global is - // "alive" as well... Remove it from the GlobalNodes list so we only have - // unreachable globals in the list. - // - Iterate = false; - if (!(Flags & DSGraph::RemoveUnreachableGlobals)) - for (unsigned i = 0; i != GlobalNodes.size(); ++i) - if (CanReachAliveNodes(GlobalNodes[i].second, Alive, Visited, - Flags & DSGraph::RemoveUnreachableGlobals)) { - std::swap(GlobalNodes[i--], GlobalNodes.back()); // Move to end to... - GlobalNodes.pop_back(); // erase efficiently - Iterate = true; - } - - // Mark only unresolvable call nodes for moving to the GlobalsGraph since - // call nodes that get resolved will be difficult to remove from that graph. - // The final unresolved call nodes must be handled specially at the end of - // the BU pass (i.e., in main or other roots of the call graph). - for (unsigned i = 0, e = AuxFunctionCalls.size(); i != e; ++i) - if (!AuxFCallsAlive[i] && - (AuxFunctionCalls[i].isIndirectCall() - || CallSiteUsesAliveArgs(AuxFunctionCalls[i], Alive, Visited, - Flags & DSGraph::RemoveUnreachableGlobals))) { - AuxFunctionCalls[i].markReachableNodes(Alive); - AuxFCallsAlive[i] = true; - Iterate = true; - } - } while (Iterate); - - // Move dead aux function calls to the end of the list - unsigned CurIdx = 0; - for (unsigned i = 0, e = AuxFunctionCalls.size(); i != e; ++i) - if (AuxFCallsAlive[i]) - AuxFunctionCalls[CurIdx++].swap(AuxFunctionCalls[i]); - - // Copy and merge all global nodes and dead aux call nodes into the - // GlobalsGraph, and all nodes reachable from those nodes - // - if (!(Flags & DSGraph::RemoveUnreachableGlobals)) { - - // First, add the dead aux call nodes to the set of root nodes for cloning - // -- return value at this call site, if any - // -- actual arguments passed at this call site - // -- callee node at this call site, if this is an indirect call - for (unsigned i = CurIdx, e = AuxFunctionCalls.size(); i != e; ++i) { - if (const DSNode* RetNode = AuxFunctionCalls[i].getRetVal().getNode()) - GlobalNodeSet.insert(RetNode); - for (unsigned j=0, N=AuxFunctionCalls[i].getNumPtrArgs(); j < N; ++j) - if (const DSNode* ArgTarget=AuxFunctionCalls[i].getPtrArg(j).getNode()) - GlobalNodeSet.insert(ArgTarget); - if (AuxFunctionCalls[i].isIndirectCall()) - GlobalNodeSet.insert(AuxFunctionCalls[i].getCalleeNode()); - } - - // There are no "pre-completed" nodes so use any empty map for those. - // Strip all alloca bits since the current function is only for the BU pass. - // Strip all incomplete bits since they are short-lived properties and they - // will be correctly computed when rematerializing nodes into the functions. - // - NodeMapTy CompletedMap; - GlobalsGraph->cloneReachableSubgraph(*this, GlobalNodeSet, - GlobalNodeMap, CompletedMap, - (DSGraph::StripAllocaBit | - DSGraph::StripIncompleteBit)); - } - - // Remove all dead aux function calls... - if (!(Flags & DSGraph::RemoveUnreachableGlobals)) { - assert(GlobalsGraph && "No globals graph available??"); - - // Copy the unreachable call nodes to the globals graph, updating - // their target pointers using the GlobalNodeMap - for (unsigned i = CurIdx, e = AuxFunctionCalls.size(); i != e; ++i) - GlobalsGraph->AuxFunctionCalls.push_back(DSCallSite(AuxFunctionCalls[i], - GlobalNodeMap)); - } - // Crop all the useless ones out... - AuxFunctionCalls.erase(AuxFunctionCalls.begin()+CurIdx, - AuxFunctionCalls.end()); - - // We are finally done with the GlobalNodeMap so we can clear it and - // then get rid of unused nodes in the GlobalsGraph produced by merging. - GlobalNodeMap.clear(); - GlobalsGraph->removeTriviallyDeadNodes(); - - // At this point, any nodes which are visited, but not alive, are nodes - // which can be removed. Loop over all nodes, eliminating completely - // unreachable nodes. - // - std::vector<DSNode*> DeadNodes; - DeadNodes.reserve(Nodes.size()); - for (unsigned i = 0; i != Nodes.size(); ++i) - if (!Alive.count(Nodes[i])) { - DSNode *N = Nodes[i]; - Nodes[i--] = Nodes.back(); // move node to end of vector - Nodes.pop_back(); // Erase node from alive list. - DeadNodes.push_back(N); - N->dropAllReferences(); - } else { - assert(Nodes[i]->getForwardNode() == 0 && "Alive forwarded node?"); - } - - // Remove all unreachable globals from the ScalarMap. - // If flag RemoveUnreachableGlobals is set, GlobalNodes has only dead nodes. - // In either case, the dead nodes will not be in the set Alive. - for (unsigned i = 0, e = GlobalNodes.size(); i != e; ++i) { - assert(((Flags & DSGraph::RemoveUnreachableGlobals) || - !Alive.count(GlobalNodes[i].second)) && "huh? non-dead global"); - if (!Alive.count(GlobalNodes[i].second)) - ScalarMap.erase(GlobalNodes[i].first); - } - - // Delete all dead nodes now since their referrer counts are zero. - for (unsigned i = 0, e = DeadNodes.size(); i != e; ++i) - delete DeadNodes[i]; - - DEBUG(AssertGraphOK(); GlobalsGraph->AssertGraphOK()); -} - -void DSGraph::AssertGraphOK() const { - for (unsigned i = 0, e = Nodes.size(); i != e; ++i) - Nodes[i]->assertOK(); - - for (ScalarMapTy::const_iterator I = ScalarMap.begin(), - E = ScalarMap.end(); I != E; ++I) { - assert(I->second.getNode() && "Null node in scalarmap!"); - AssertNodeInGraph(I->second.getNode()); - if (GlobalValue *GV = dyn_cast<GlobalValue>(I->first)) { - assert(I->second.getNode()->isGlobalNode() && - "Global points to node, but node isn't global?"); - AssertNodeContainsGlobal(I->second.getNode(), GV); - } - } - AssertCallNodesInGraph(); - AssertAuxCallNodesInGraph(); -} - -/// mergeInGlobalsGraph - This method is useful for clients to incorporate the -/// globals graph into the DS, BU or TD graph for a function. This code retains -/// all globals, i.e., does not delete unreachable globals after they are -/// inlined. -/// -void DSGraph::mergeInGlobalsGraph() { - NodeMapTy GlobalNodeMap; - ScalarMapTy OldValMap; - ReturnNodesTy OldRetNodes; - cloneInto(*GlobalsGraph, OldValMap, OldRetNodes, GlobalNodeMap, - DSGraph::KeepAllocaBit | DSGraph::DontCloneCallNodes | - DSGraph::DontCloneAuxCallNodes); - - // Now merge existing global nodes in the GlobalsGraph with their copies - for (ScalarMapTy::iterator I = ScalarMap.begin(), E = ScalarMap.end(); - I != E; ++I) - if (isa<GlobalValue>(I->first)) { // Found a global node - DSNodeHandle &GH = I->second; - DSNodeHandle &GGNodeH = GlobalsGraph->getScalarMap()[I->first]; - GH.mergeWith(GlobalNodeMap[GGNodeH.getNode()]); - } - - // Merging leaves behind unused nodes: get rid of them now. - GlobalNodeMap.clear(); - OldValMap.clear(); - OldRetNodes.clear(); - removeTriviallyDeadNodes(); -}
diff --git a/poolalloc/lib/DSA/DataStructureAA.cpp b/poolalloc/lib/DSA/DataStructureAA.cpp deleted file mode 100644 index e7e963a..0000000 --- a/poolalloc/lib/DSA/DataStructureAA.cpp +++ /dev/null
@@ -1,171 +0,0 @@ -//===- DataStructureAA.cpp - Data Structure Based Alias Analysis ----------===// -// -// This pass uses the top-down data structure graphs to implement a simple -// context sensitive alias analysis. -// -//===----------------------------------------------------------------------===// - -#include "llvm/Analysis/DataStructure.h" -#include "llvm/Analysis/DSGraph.h" -#include "llvm/Analysis/AliasAnalysis.h" -#include "llvm/Module.h" - -namespace { - class DSAA : public Pass, public AliasAnalysis { - TDDataStructures *TD; - public: - DSAA() : TD(0) {} - - //------------------------------------------------ - // Implement the Pass API - // - - // run - Build up the result graph, representing the pointer graph for the - // program. - // - bool run(Module &M) { - InitializeAliasAnalysis(this); - TD = &getAnalysis<TDDataStructures>(); - return false; - } - - virtual void getAnalysisUsage(AnalysisUsage &AU) const { - AliasAnalysis::getAnalysisUsage(AU); - AU.setPreservesAll(); // Does not transform code... - AU.addRequired<TDDataStructures>(); // Uses TD Datastructures - AU.addRequired<AliasAnalysis>(); // Chains to another AA impl... - } - - //------------------------------------------------ - // Implement the AliasAnalysis API - // - - AliasResult alias(const Value *V1, unsigned V1Size, - const Value *V2, unsigned V2Size); - - void getMustAliases(Value *P, std::vector<Value*> &RetVals); - - private: - DSGraph *getGraphForValue(const Value *V); - }; - - // Register the pass... - RegisterOpt<DSAA> X("ds-aa", "Data Structure Graph Based Alias Analysis"); - - // Register as an implementation of AliasAnalysis - RegisterAnalysisGroup<AliasAnalysis, DSAA> Y; -} - -// getGraphForValue - Return the DSGraph to use for queries about the specified -// value... -// -DSGraph *DSAA::getGraphForValue(const Value *V) { - if (const Instruction *I = dyn_cast<Instruction>(V)) - return &TD->getDSGraph(*I->getParent()->getParent()); - else if (const Argument *A = dyn_cast<Argument>(V)) - return &TD->getDSGraph(*A->getParent()); - else if (const BasicBlock *BB = dyn_cast<BasicBlock>(V)) - return &TD->getDSGraph(*BB->getParent()); - return 0; -} - -// isSinglePhysicalObject - For now, the only case that we know that there is -// only one memory object in the node is when there is a single global in the -// node, and the only composition bit set is Global. -// -static bool isSinglePhysicalObject(DSNode *N) { - assert(N->isComplete() && "Can only tell if this is a complete object!"); - return N->isGlobalNode() && N->getGlobals().size() == 1 && - !N->isHeapNode() && !N->isAllocaNode() && !N->isUnknownNode(); -} - -// alias - This is the only method here that does anything interesting... -AliasAnalysis::AliasResult DSAA::alias(const Value *V1, unsigned V1Size, - const Value *V2, unsigned V2Size) { - if (V1 == V2) return MustAlias; - - DSGraph *G1 = getGraphForValue(V1); - DSGraph *G2 = getGraphForValue(V2); - assert((!G1 || !G2 || G1 == G2) && "Alias query for 2 different functions?"); - - // Get the graph to use... - DSGraph &G = *(G1 ? G1 : (G2 ? G2 : &TD->getGlobalsGraph())); - - const DSGraph::ScalarMapTy &GSM = G.getScalarMap(); - DSGraph::ScalarMapTy::const_iterator I = GSM.find((Value*)V1); - if (I != GSM.end()) { - assert(I->second.getNode() && "Scalar map points to null node?"); - DSGraph::ScalarMapTy::const_iterator J = GSM.find((Value*)V2); - if (J != GSM.end()) { - assert(J->second.getNode() && "Scalar map points to null node?"); - - DSNode *N1 = I->second.getNode(), *N2 = J->second.getNode(); - unsigned O1 = I->second.getOffset(), O2 = J->second.getOffset(); - - // We can only make a judgement of one of the nodes is complete... - if (N1->isComplete() || N2->isComplete()) { - if (N1 != N2) - return NoAlias; // Completely different nodes. - -#if 0 // This does not correctly handle arrays! - // Both point to the same node and same offset, and there is only one - // physical memory object represented in the node, return must alias. - // - // FIXME: This isn't correct because we do not handle array indexing - // correctly. - - if (O1 == O2 && isSinglePhysicalObject(N1)) - return MustAlias; // Exactly the same object & offset -#endif - - // See if they point to different offsets... if so, we may be able to - // determine that they do not alias... - if (O1 != O2) { - if (O2 < O1) { // Ensure that O1 <= O2 - std::swap(V1, V2); - std::swap(O1, O2); - std::swap(V1Size, V2Size); - } - - // FIXME: This is not correct because we do not handle array - // indexing correctly with this check! - //if (O1+V1Size <= O2) return NoAlias; - } - } - } - } - - // FIXME: we could improve on this by checking the globals graph for aliased - // global queries... - return getAnalysis<AliasAnalysis>().alias(V1, V1Size, V2, V2Size); -} - - -/// getMustAliases - If there are any pointers known that must alias this -/// pointer, return them now. This allows alias-set based alias analyses to -/// perform a form a value numbering (which is exposed by load-vn). If an alias -/// analysis supports this, it should ADD any must aliased pointers to the -/// specified vector. -/// -void DSAA::getMustAliases(Value *P, std::vector<Value*> &RetVals) { -#if 0 // This does not correctly handle arrays! - // Currently the only must alias information we can provide is to say that - // something is equal to a global value. If we already have a global value, - // don't get worked up about it. - if (!isa<GlobalValue>(P)) { - DSGraph *G = getGraphForValue(P); - if (!G) G = &TD->getGlobalsGraph(); - - // The only must alias information we can currently determine occurs when - // the node for P is a global node with only one entry. - const DSGraph::ScalarMapTy &GSM = G->getScalarMap(); - DSGraph::ScalarMapTy::const_iterator I = GSM.find(P); - if (I != GSM.end()) { - DSNode *N = I->second.getNode(); - if (N->isComplete() && isSinglePhysicalObject(N)) - RetVals.push_back(N->getGlobals()[0]); - } - } -#endif - return getAnalysis<AliasAnalysis>().getMustAliases(P, RetVals); -}
diff --git a/poolalloc/lib/DSA/DataStructureOpt.cpp b/poolalloc/lib/DSA/DataStructureOpt.cpp deleted file mode 100644 index 297979a..0000000 --- a/poolalloc/lib/DSA/DataStructureOpt.cpp +++ /dev/null
@@ -1,91 +0,0 @@ -//===- DataStructureOpt.cpp - Data Structure Analysis Based Optimizations -===// -// -// This pass uses DSA to a series of simple optimizations, like marking -// unwritten global variables 'constant'. -// -//===----------------------------------------------------------------------===// - -#include "llvm/Analysis/DataStructure.h" -#include "llvm/Analysis/DSGraph.h" -#include "llvm/Module.h" -#include "llvm/Constant.h" -#include "Support/Statistic.h" - -namespace { - Statistic<> - NumGlobalsConstanted("ds-opt", "Number of globals marked constant"); - Statistic<> - NumGlobalsIsolated("ds-opt", "Number of globals with references dropped"); - - class DSOpt : public Pass { - TDDataStructures *TD; - public: - bool run(Module &M) { - TD = &getAnalysis<TDDataStructures>(); - bool Changed = OptimizeGlobals(M); - return Changed; - } - - virtual void getAnalysisUsage(AnalysisUsage &AU) const { - AU.addRequired<TDDataStructures>(); // Uses TD Datastructures - AU.addPreserved<LocalDataStructures>(); // Preserves local... - AU.addPreserved<TDDataStructures>(); // Preserves bu... - AU.addPreserved<BUDataStructures>(); // Preserves td... - } - - private: - bool OptimizeGlobals(Module &M); - }; - - RegisterOpt<DSOpt> X("ds-opt", "DSA-based simple optimizations"); -} - - -/// OptimizeGlobals - This method uses information taken from DSA to optimize -/// global variables. -/// -bool DSOpt::OptimizeGlobals(Module &M) { - DSGraph &GG = TD->getGlobalsGraph(); - const DSGraph::ScalarMapTy &SM = GG.getScalarMap(); - bool Changed = false; - - for (Module::giterator I = M.gbegin(), E = M.gend(); I != E; ++I) - if (!I->isExternal()) { // Loop over all of the non-external globals... - // Look up the node corresponding to this global, if it exists. - DSNode *GNode = 0; - DSGraph::ScalarMapTy::const_iterator SMI = SM.find(I); - if (SMI != SM.end()) GNode = SMI->second.getNode(); - - if (GNode == 0 && I->hasInternalLinkage()) { - // If there is no entry in the scalar map for this global, it was never - // referenced in the program. If it has internal linkage, that means we - // can delete it. We don't ACTUALLY want to delete the global, just - // remove anything that references the global: later passes will take - // care of nuking it. - if (!I->use_empty()) { - I->replaceAllUsesWith(Constant::getNullValue((Type*)I->getType())); - ++NumGlobalsIsolated; - } - } else if (GNode && GNode->isComplete()) { - - // If the node has not been read or written, and it is not externally - // visible, kill any references to it so it can be DCE'd. - if (!GNode->isModified() && !GNode->isRead() &&I->hasInternalLinkage()){ - if (!I->use_empty()) { - I->replaceAllUsesWith(Constant::getNullValue((Type*)I->getType())); - ++NumGlobalsIsolated; - } - } - - // We expect that there will almost always be a node for this global. - // If there is, and the node doesn't have the M bit set, we can set the - // 'constant' bit on the global. - if (!GNode->isModified() && !I->isConstant()) { - I->setConstant(true); - ++NumGlobalsConstanted; - Changed = true; - } - } - } - return Changed; -}
diff --git a/poolalloc/lib/DSA/DataStructureStats.cpp b/poolalloc/lib/DSA/DataStructureStats.cpp deleted file mode 100644 index c2ca7ea..0000000 --- a/poolalloc/lib/DSA/DataStructureStats.cpp +++ /dev/null
@@ -1,90 +0,0 @@ -//===- DSGraphStats.cpp - Various statistics for DS Graphs -----*- C++ -*--===// -// -//===----------------------------------------------------------------------===// - -#include "llvm/Analysis/DataStructure.h" -#include "llvm/Analysis/DSGraph.h" -#include "llvm/Function.h" -#include "llvm/iOther.h" -#include "llvm/Pass.h" -#include "Support/Statistic.h" -#include <vector> - -namespace { - Statistic<> TotalNumCallees("totalcallees", - "Total number of callee functions at all indirect call sites"); - Statistic<> NumIndirectCalls("numindirect", - "Total number of indirect call sites in the program"); - Statistic<> NumPoolNodes("numpools", - "Number of allocation nodes that could be pool allocated"); - - class DSGraphStats: public FunctionPass { - void countCallees(const Function& F, const DSGraph& tdGraph); - - public: - /// Driver functions to compute the Load/Store Dep. Graph per function. - bool runOnFunction(Function& F); - - /// getAnalysisUsage - This modify nothing, and uses the Top-Down Graph. - void getAnalysisUsage(AnalysisUsage &AU) const { - AU.setPreservesAll(); - AU.addRequired<TDDataStructures>(); - } - - /// Debugging support methods - void print(std::ostream &O) const { } - void dump() const; - }; - - static RegisterAnalysis<DSGraphStats> Z("dsstats", "DS Graph Statistics"); -} - -static bool isIndirectCallee(Value *V) { - if (isa<Function>(V)) return false; - - if (CastInst *CI = dyn_cast<CastInst>(V)) - return isIndirectCallee(CI->getOperand(0)); - return true; -} - - -void DSGraphStats::countCallees(const Function& F, const DSGraph& tdGraph) { - unsigned numIndirectCalls = 0, totalNumCallees = 0; - - const std::vector<DSCallSite>& callSites = tdGraph.getFunctionCalls(); - for (unsigned i=0, N = callSites.size(); i < N; ++i) - if (isIndirectCallee(callSites[i].getCallInst().getCalledValue())) - { // This is an indirect function call - std::vector<GlobalValue*> Callees = - callSites[i].getCalleeNode()->getGlobals(); - if (Callees.size() > 0) { - totalNumCallees += Callees.size(); - ++numIndirectCalls; - } -#ifndef NDEBUG - else - std::cerr << "WARNING: No callee in Function " << F.getName() - << "at call:\n" << callSites[i].getCallInst(); -#endif - } - - TotalNumCallees += totalNumCallees; - NumIndirectCalls += numIndirectCalls; - - if (numIndirectCalls) - std::cout << " In function " << F.getName() << ": " - << (totalNumCallees / (double) numIndirectCalls) - << " average callees per indirect call\n"; -} - - -bool DSGraphStats::runOnFunction(Function& F) { - const DSGraph& tdGraph = getAnalysis<TDDataStructures>().getDSGraph(F); - countCallees(F, tdGraph); - return true; -} - -void DSGraphStats::dump() const -{ - this->print(std::cerr); -}
diff --git a/poolalloc/lib/DSA/GraphChecker.cpp b/poolalloc/lib/DSA/GraphChecker.cpp deleted file mode 100644 index 4c690b1..0000000 --- a/poolalloc/lib/DSA/GraphChecker.cpp +++ /dev/null
@@ -1,188 +0,0 @@ -//===- GraphChecker.cpp - Assert that various graph properties hold -------===// -// -// This pass is used to test DSA with regression tests. It can be used to check -// that certain graph properties hold, such as two nodes being disjoint, whether -// or not a node is collapsed, etc. These are the command line arguments that -// it supports: -// -// --dsgc-dsapass={local,bu,td} - Specify what flavor of graph to check -// --dsgc-abort-if-any-collapsed - Abort if any collapsed nodes are found -// --dsgc-abort-if-collapsed=<list> - Abort if a node pointed to by an SSA -// value with name in <list> is collapsed -// --dsgc-check-flags=<list> - Abort if the specified nodes have flags -// that are not specified. -// --dsgc-abort-if-merged=<list> - Abort if any of the named SSA values -// point to the same node. -// -//===----------------------------------------------------------------------===// - -#include "llvm/Analysis/DataStructure.h" -#include "llvm/Analysis/DSGraph.h" -#include "Support/CommandLine.h" -#include "llvm/Value.h" -#include <set> - -namespace { - enum DSPass { local, bu, td }; - cl::opt<DSPass> - DSPass("dsgc-dspass", cl::Hidden, - cl::desc("Specify which DSA pass the -datastructure-gc pass should use"), - cl::values(clEnumVal(local, "Local pass"), - clEnumVal(bu, "Bottom-up pass"), - clEnumVal(td, "Top-down pass"), 0), cl::init(local)); - - cl::opt<bool> - AbortIfAnyCollapsed("dsgc-abort-if-any-collapsed", cl::Hidden, - cl::desc("Abort if any collapsed nodes are found")); - cl::list<std::string> - AbortIfCollapsed("dsgc-abort-if-collapsed", cl::Hidden, cl::CommaSeparated, - cl::desc("Abort if any of the named symbols is collapsed")); - cl::list<std::string> - CheckFlags("dsgc-check-flags", cl::Hidden, cl::CommaSeparated, - cl::desc("Check that flags are specified for nodes")); - cl::list<std::string> - AbortIfMerged("dsgc-abort-if-merged", cl::Hidden, cl::CommaSeparated, - cl::desc("Abort if any of the named symbols are merged together")); - - struct DSGC : public FunctionPass { - DSGC(); - bool doFinalization(Module &M); - bool runOnFunction(Function &F); - - virtual void getAnalysisUsage(AnalysisUsage &AU) const { - switch (DSPass) { - case local: AU.addRequired<LocalDataStructures>(); break; - case bu: AU.addRequired<BUDataStructures>(); break; - case td: AU.addRequired<TDDataStructures>(); break; - } - AU.setPreservesAll(); - } - void print(std::ostream &O, const Module *M) const {} - - private: - void verify(const DSGraph &G); - }; - - RegisterAnalysis<DSGC> X("datastructure-gc", "DSA Graph Checking Pass"); -} - -DSGC::DSGC() { - if (!AbortIfAnyCollapsed && AbortIfCollapsed.empty() && - CheckFlags.empty() && AbortIfMerged.empty()) { - std::cerr << "The -datastructure-gc is useless if you don't specify any" - " -dsgc-* options. See the -help-hidden output for a list.\n"; - abort(); - } -} - - -/// doFinalization - Verify that the globals graph is in good shape... -/// -bool DSGC::doFinalization(Module &M) { - switch (DSPass) { - case local:verify(getAnalysis<LocalDataStructures>().getGlobalsGraph());break; - case bu: verify(getAnalysis<BUDataStructures>().getGlobalsGraph()); break; - case td: verify(getAnalysis<TDDataStructures>().getGlobalsGraph()); break; - } - return false; -} - -/// runOnFunction - Get the DSGraph for this function and verify that it is ok. -/// -bool DSGC::runOnFunction(Function &F) { - switch (DSPass) { - case local: verify(getAnalysis<LocalDataStructures>().getDSGraph(F)); break; - case bu: verify(getAnalysis<BUDataStructures>().getDSGraph(F)); break; - case td: verify(getAnalysis<TDDataStructures>().getDSGraph(F)); break; - } - - return false; -} - -/// verify - This is the function which checks to make sure that all of the -/// invariants established on the command line are true. -/// -void DSGC::verify(const DSGraph &G) { - // Loop over all of the nodes, checking to see if any are collapsed... - if (AbortIfAnyCollapsed) { - const std::vector<DSNode*> &Nodes = G.getNodes(); - for (unsigned i = 0, e = Nodes.size(); i != e; ++i) - if (Nodes[i]->isNodeCompletelyFolded()) { - std::cerr << "Node is collapsed: "; - Nodes[i]->print(std::cerr, &G); - abort(); - } - } - - if (!AbortIfCollapsed.empty() || !CheckFlags.empty() || - !AbortIfMerged.empty()) { - // Convert from a list to a set, because we don't have cl::set's yet. FIXME - std::set<std::string> AbortIfCollapsedS(AbortIfCollapsed.begin(), - AbortIfCollapsed.end()); - std::set<std::string> AbortIfMergedS(AbortIfMerged.begin(), - AbortIfMerged.end()); - std::map<std::string, unsigned> CheckFlagsM; - - for (cl::list<std::string>::iterator I = CheckFlags.begin(), - E = CheckFlags.end(); I != E; ++I) { - unsigned ColonPos = I->rfind(':'); - if (ColonPos == std::string::npos) { - std::cerr << "Error: '" << *I - << "' is an invalid value for the --dsgc-check-flags option!\n"; - abort(); - } - - unsigned Flags = 0; - for (unsigned C = ColonPos+1; C != I->size(); ++C) - switch ((*I)[C]) { - case 'S': Flags |= DSNode::AllocaNode; break; - case 'H': Flags |= DSNode::HeapNode; break; - case 'G': Flags |= DSNode::GlobalNode; break; - case 'U': Flags |= DSNode::UnknownNode; break; - case 'I': Flags |= DSNode::Incomplete; break; - case 'M': Flags |= DSNode::Modified; break; - case 'R': Flags |= DSNode::Read; break; - case 'A': Flags |= DSNode::Array; break; - default: std::cerr << "Invalid DSNode flag!\n"; abort(); - } - CheckFlagsM[std::string(I->begin(), I->begin()+ColonPos)] = Flags; - } - - // Now we loop over all of the scalars, checking to see if any are collapsed - // that are not supposed to be, or if any are merged together. - const DSGraph::ScalarMapTy &SM = G.getScalarMap(); - std::map<DSNode*, std::string> AbortIfMergedNodes; - - for (DSGraph::ScalarMapTy::const_iterator I = SM.begin(), E = SM.end(); - I != E; ++I) - if (I->first->hasName() && I->second.getNode()) { - const std::string &Name = I->first->getName(); - DSNode *N = I->second.getNode(); - - // Verify it is not collapsed if it is not supposed to be... - if (N->isNodeCompletelyFolded() && AbortIfCollapsedS.count(Name)) { - std::cerr << "Node for value '%" << Name << "' is collapsed: "; - N->print(std::cerr, &G); - abort(); - } - - if (CheckFlagsM.count(Name) && CheckFlagsM[Name] != N->getNodeFlags()) { - std::cerr << "Node flags are not as expected for node: " << Name - << "\n"; - N->print(std::cerr, &G); - abort(); - } - - // Verify that it is not merged if it is not supposed to be... - if (AbortIfMergedS.count(Name)) { - if (AbortIfMergedNodes.count(N)) { - std::cerr << "Nodes for values '%" << Name << "' and '%" - << AbortIfMergedNodes[N] << "' is merged: "; - N->print(std::cerr, &G); - abort(); - } - AbortIfMergedNodes[N] = Name; - } - } - } -}
diff --git a/poolalloc/lib/DSA/IPModRef.cpp b/poolalloc/lib/DSA/IPModRef.cpp deleted file mode 100644 index 86bddc8..0000000 --- a/poolalloc/lib/DSA/IPModRef.cpp +++ /dev/null
@@ -1,439 +0,0 @@ -//===- IPModRef.cpp - Compute IP Mod/Ref information ------------*- C++ -*-===// -// -// See high-level comments in include/llvm/Analysis/IPModRef.h -// -//===----------------------------------------------------------------------===// - -#include "llvm/Analysis/IPModRef.h" -#include "llvm/Analysis/DataStructure.h" -#include "llvm/Analysis/DSGraph.h" -#include "llvm/Module.h" -#include "llvm/Function.h" -#include "llvm/iMemory.h" -#include "llvm/iOther.h" -#include "Support/Statistic.h" -#include "Support/STLExtras.h" -#include "Support/StringExtras.h" -#include <vector> - -//---------------------------------------------------------------------------- -// Private constants and data -//---------------------------------------------------------------------------- - -static RegisterAnalysis<IPModRef> -Z("ipmodref", "Interprocedural mod/ref analysis"); - - -//---------------------------------------------------------------------------- -// class ModRefInfo -//---------------------------------------------------------------------------- - -void ModRefInfo::print(std::ostream &O, - const std::string& sprefix) const -{ - O << sprefix << "Modified nodes = " << modNodeSet; - O << sprefix << "Referenced nodes = " << refNodeSet; -} - -void ModRefInfo::dump() const -{ - print(std::cerr); -} - -//---------------------------------------------------------------------------- -// class FunctionModRefInfo -//---------------------------------------------------------------------------- - - -// This constructor computes a node numbering for the TD graph. -// -FunctionModRefInfo::FunctionModRefInfo(const Function& func, - IPModRef& ipmro, - DSGraph* tdgClone) - : F(func), IPModRefObj(ipmro), - funcTDGraph(tdgClone), - funcModRefInfo(tdgClone->getGraphSize()) -{ - for (unsigned i=0, N = funcTDGraph->getGraphSize(); i < N; ++i) - NodeIds[funcTDGraph->getNodes()[i]] = i; -} - - -FunctionModRefInfo::~FunctionModRefInfo() -{ - for(std::map<const CallInst*, ModRefInfo*>::iterator - I=callSiteModRefInfo.begin(), E=callSiteModRefInfo.end(); I != E; ++I) - delete(I->second); - - // Empty map just to make problems easier to track down - callSiteModRefInfo.clear(); - - delete funcTDGraph; -} - -unsigned FunctionModRefInfo::getNodeId(const Value* value) const { - return getNodeId(funcTDGraph->getNodeForValue(const_cast<Value*>(value)) - .getNode()); -} - - - -// Compute Mod/Ref bit vectors for the entire function. -// These are simply copies of the Read/Write flags from the nodes of -// the top-down DS graph. -// -void FunctionModRefInfo::computeModRef(const Function &func) -{ - // Mark all nodes in the graph that are marked MOD as being mod - // and all those marked REF as being ref. - for (unsigned i = 0, N = funcTDGraph->getGraphSize(); i < N; ++i) - { - if (funcTDGraph->getNodes()[i]->isModified()) - funcModRefInfo.setNodeIsMod(i); - if (funcTDGraph->getNodes()[i]->isRead()) - funcModRefInfo.setNodeIsRef(i); - } - - // Compute the Mod/Ref info for all call sites within the function. - // The call sites are recorded in the TD graph. - const std::vector<DSCallSite>& callSites = funcTDGraph->getFunctionCalls(); - for (unsigned i = 0, N = callSites.size(); i < N; ++i) - computeModRef(callSites[i].getCallInst()); -} - - -// ResolveCallSiteModRefInfo - This method performs the following actions: -// -// 1. It clones the top-down graph for the current function -// 2. It clears all of the mod/ref bits in the cloned graph -// 3. It then merges the bottom-up graph(s) for the specified call-site into -// the clone (bringing new mod/ref bits). -// 4. It returns the clone, and a mapping of nodes from the original TDGraph to -// the cloned graph with Mod/Ref info for the callsite. -// -// NOTE: Because this clones a dsgraph and returns it, the caller is responsible -// for deleting the returned graph! -// NOTE: This method may return a null pointer if it is unable to determine the -// requested information (because the call site calls an external -// function or we cannot determine the complete set of functions invoked). -// -DSGraph* FunctionModRefInfo::ResolveCallSiteModRefInfo(CallInst &CI, - hash_map<const DSNode*, DSNodeHandle> &NodeMap) -{ - // Step #0: Quick check if we are going to fail anyway: avoid - // all the graph cloning and map copying in steps #1 and #2. - // - if (const Function *F = CI.getCalledFunction()) - { - if (F->isExternal()) - return 0; // We cannot compute Mod/Ref info for this callsite... - } - else - { - // Eventually, should check here if any callee is external. - // For now we are not handling this case anyway. - std::cerr << "IP Mod/Ref indirect call not implemented yet: " - << "Being conservative\n"; - return 0; // We cannot compute Mod/Ref info for this callsite... - } - - // Step #1: Clone the top-down graph... - DSGraph *Result = new DSGraph(*funcTDGraph, NodeMap); - - // Step #2: Clear Mod/Ref information... - Result->maskNodeTypes(~(DSNode::Modified | DSNode::Read)); - - // Step #3: clone the bottom up graphs for the callees into the caller graph - if (Function *F = CI.getCalledFunction()) - { - assert(!F->isExternal()); - - // Build up a DSCallSite for our invocation point here... - - // If the call returns a value, make sure to merge the nodes... - DSNodeHandle RetVal; - if (DS::isPointerType(CI.getType())) - RetVal = Result->getNodeForValue(&CI); - - // Populate the arguments list... - std::vector<DSNodeHandle> Args; - for (unsigned i = 1, e = CI.getNumOperands(); i != e; ++i) - if (DS::isPointerType(CI.getOperand(i)->getType())) - Args.push_back(Result->getNodeForValue(CI.getOperand(i))); - - // Build the call site... - DSCallSite CS(CI, RetVal, F, Args); - - // Perform the merging now of the graph for the callee, which will - // come with mod/ref bits set... - Result->mergeInGraph(CS, *F, IPModRefObj.getBUDSGraph(*F), - DSGraph::StripAllocaBit - | DSGraph::DontCloneCallNodes - | DSGraph::DontCloneAuxCallNodes); - } - else - assert(0 && "See error message"); - - // Remove dead nodes aggressively to match the caller's original graph. - Result->removeDeadNodes(DSGraph::KeepUnreachableGlobals); - - // Step #4: Return the clone + the mapping (by ref) - return Result; -} - -// Compute Mod/Ref bit vectors for a single call site. -// These are copies of the Read/Write flags from the nodes of -// the graph produced by clearing all flags in teh caller's TD graph -// and then inlining the callee's BU graph into the caller's TD graph. -// -void -FunctionModRefInfo::computeModRef(const CallInst& callInst) -{ - // Allocate the mod/ref info for the call site. Bits automatically cleared. - ModRefInfo* callModRefInfo = new ModRefInfo(funcTDGraph->getGraphSize()); - callSiteModRefInfo[&callInst] = callModRefInfo; - - // Get a copy of the graph for the callee with the callee inlined - hash_map<const DSNode*, DSNodeHandle> NodeMap; - DSGraph* csgp = ResolveCallSiteModRefInfo(const_cast<CallInst&>(callInst), - NodeMap); - if (!csgp) - { // Callee's side effects are unknown: mark all nodes Mod and Ref. - // Eventually this should only mark nodes visible to the callee, i.e., - // exclude stack variables not reachable from any outgoing argument - // or any global. - callModRefInfo->getModSet().set(); - callModRefInfo->getRefSet().set(); - return; - } - - // For all nodes in the graph, extract the mod/ref information - const std::vector<DSNode*>& csgNodes = csgp->getNodes(); - const std::vector<DSNode*>& origNodes = funcTDGraph->getNodes(); - assert(csgNodes.size() == origNodes.size()); - for (unsigned i=0, N = origNodes.size(); i < N; ++i) - { - DSNode* csgNode = NodeMap[origNodes[i]].getNode(); - assert(csgNode && "Inlined and original graphs do not correspond!"); - if (csgNode->isModified()) - callModRefInfo->setNodeIsMod(getNodeId(origNodes[i])); - if (csgNode->isRead()) - callModRefInfo->setNodeIsRef(getNodeId(origNodes[i])); - } - - // Drop nodemap before we delete the graph... - NodeMap.clear(); - delete csgp; -} - - -class DSGraphPrintHelper { - const DSGraph& tdGraph; - std::vector<std::vector<const Value*> > knownValues; // identifiable objects - -public: - /*ctor*/ DSGraphPrintHelper(const FunctionModRefInfo& fmrInfo) - : tdGraph(fmrInfo.getFuncGraph()) - { - knownValues.resize(tdGraph.getGraphSize()); - - // For every identifiable value, save Value pointer in knownValues[i] - for (hash_map<Value*, DSNodeHandle>::const_iterator - I = tdGraph.getScalarMap().begin(), - E = tdGraph.getScalarMap().end(); I != E; ++I) - if (isa<GlobalValue>(I->first) || - isa<Argument>(I->first) || - isa<LoadInst>(I->first) || - isa<AllocaInst>(I->first) || - isa<MallocInst>(I->first)) - { - unsigned nodeId = fmrInfo.getNodeId(I->second.getNode()); - knownValues[nodeId].push_back(I->first); - } - } - - void printValuesInBitVec(std::ostream &O, const BitSetVector& bv) const - { - assert(bv.size() == knownValues.size()); - - if (bv.none()) - { // No bits are set: just say so and return - O << "\tNONE.\n"; - return; - } - - if (bv.all()) - { // All bits are set: just say so and return - O << "\tALL GRAPH NODES.\n"; - return; - } - - for (unsigned i=0, N=bv.size(); i < N; ++i) - if (bv.test(i)) - { - O << "\tNode# " << i << " : "; - if (! knownValues[i].empty()) - for (unsigned j=0, NV=knownValues[i].size(); j < NV; j++) - { - const Value* V = knownValues[i][j]; - - if (isa<GlobalValue>(V)) O << "(Global) "; - else if (isa<Argument>(V)) O << "(Target of FormalParm) "; - else if (isa<LoadInst>(V)) O << "(Target of LoadInst ) "; - else if (isa<AllocaInst>(V)) O << "(Target of AllocaInst) "; - else if (isa<MallocInst>(V)) O << "(Target of MallocInst) "; - - if (V->hasName()) O << V->getName(); - else if (isa<Instruction>(V)) O << *V; - else O << "(Value*) 0x" << (void*) V; - - O << std::string((j < NV-1)? "; " : "\n"); - } - else - tdGraph.getNodes()[i]->print(O, /*graph*/ NULL); - } - } -}; - - -// Print the results of the pass. -// Currently this just prints bit-vectors and is not very readable. -// -void FunctionModRefInfo::print(std::ostream &O) const -{ - DSGraphPrintHelper DPH(*this); - - O << "========== Mod/ref information for function " - << F.getName() << "========== \n\n"; - - // First: Print Globals and Locals modified anywhere in the function. - // - O << " -----Mod/Ref in the body of function " << F.getName()<< ":\n"; - - O << " --Objects modified in the function body:\n"; - DPH.printValuesInBitVec(O, funcModRefInfo.getModSet()); - - O << " --Objects referenced in the function body:\n"; - DPH.printValuesInBitVec(O, funcModRefInfo.getRefSet()); - - O << " --Mod and Ref vectors for the nodes listed above:\n"; - funcModRefInfo.print(O, "\t"); - - O << "\n"; - - // Second: Print Globals and Locals modified at each call site in function - // - for (std::map<const CallInst*, ModRefInfo*>::const_iterator - CI = callSiteModRefInfo.begin(), CE = callSiteModRefInfo.end(); - CI != CE; ++CI) - { - O << " ----Mod/Ref information for call site\n" << CI->first; - - O << " --Objects modified at call site:\n"; - DPH.printValuesInBitVec(O, CI->second->getModSet()); - - O << " --Objects referenced at call site:\n"; - DPH.printValuesInBitVec(O, CI->second->getRefSet()); - - O << " --Mod and Ref vectors for the nodes listed above:\n"; - CI->second->print(O, "\t"); - - O << "\n"; - } - - O << "\n"; -} - -void FunctionModRefInfo::dump() const -{ - print(std::cerr); -} - - -//---------------------------------------------------------------------------- -// class IPModRef: An interprocedural pass that computes IP Mod/Ref info. -//---------------------------------------------------------------------------- - -// Free the FunctionModRefInfo objects cached in funcToModRefInfoMap. -// -void IPModRef::releaseMemory() -{ - for(std::map<const Function*, FunctionModRefInfo*>::iterator - I=funcToModRefInfoMap.begin(), E=funcToModRefInfoMap.end(); I != E; ++I) - delete(I->second); - - // Clear map so memory is not re-released if we are called again - funcToModRefInfoMap.clear(); -} - -// Run the "interprocedural" pass on each function. This needs to do -// NO real interprocedural work because all that has been done the -// data structure analysis. -// -bool IPModRef::run(Module &theModule) -{ - M = &theModule; - - for (Module::const_iterator FI = M->begin(), FE = M->end(); FI != FE; ++FI) - if (! FI->isExternal()) - getFuncInfo(*FI, /*computeIfMissing*/ true); - return true; -} - - -FunctionModRefInfo& IPModRef::getFuncInfo(const Function& func, - bool computeIfMissing) -{ - FunctionModRefInfo*& funcInfo = funcToModRefInfoMap[&func]; - assert (funcInfo != NULL || computeIfMissing); - if (funcInfo == NULL) - { // Create a new FunctionModRefInfo object. - // Clone the top-down graph and remove any dead nodes first, because - // otherwise original and merged graphs will not match. - // The memory for this graph clone will be freed by FunctionModRefInfo. - DSGraph* funcTDGraph = - new DSGraph(getAnalysis<TDDataStructures>().getDSGraph(func)); - funcTDGraph->removeDeadNodes(DSGraph::KeepUnreachableGlobals); - - funcInfo = new FunctionModRefInfo(func, *this, funcTDGraph); //auto-insert - funcInfo->computeModRef(func); // computes the mod/ref info - } - return *funcInfo; -} - -/// getBUDSGraph - This method returns the BU data structure graph for F through -/// the use of the BUDataStructures object. -/// -const DSGraph &IPModRef::getBUDSGraph(const Function &F) { - return getAnalysis<BUDataStructures>().getDSGraph(F); -} - - -// getAnalysisUsage - This pass requires top-down data structure graphs. -// It modifies nothing. -// -void IPModRef::getAnalysisUsage(AnalysisUsage &AU) const { - AU.setPreservesAll(); - AU.addRequired<LocalDataStructures>(); - AU.addRequired<BUDataStructures>(); - AU.addRequired<TDDataStructures>(); -} - - -void IPModRef::print(std::ostream &O) const -{ - O << "\nRESULTS OF INTERPROCEDURAL MOD/REF ANALYSIS:\n\n"; - - for (std::map<const Function*, FunctionModRefInfo*>::const_iterator - mapI = funcToModRefInfoMap.begin(), mapE = funcToModRefInfoMap.end(); - mapI != mapE; ++mapI) - mapI->second->print(O); - - O << "\n"; -} - - -void IPModRef::dump() const -{ - print(std::cerr); -}
diff --git a/poolalloc/lib/DSA/Local.cpp b/poolalloc/lib/DSA/Local.cpp deleted file mode 100644 index a03354d..0000000 --- a/poolalloc/lib/DSA/Local.cpp +++ /dev/null
@@ -1,503 +0,0 @@ -//===- Local.cpp - Compute a local data structure graph for a function ----===// -// -// Compute the local version of the data structure graph for a function. The -// external interface to this file is the DSGraph constructor. -// -//===----------------------------------------------------------------------===// - -#include "llvm/Analysis/DataStructure.h" -#include "llvm/Analysis/DSGraph.h" -#include "llvm/iMemory.h" -#include "llvm/iTerminators.h" -#include "llvm/iPHINode.h" -#include "llvm/iOther.h" -#include "llvm/Constants.h" -#include "llvm/DerivedTypes.h" -#include "llvm/Function.h" -#include "llvm/GlobalVariable.h" -#include "llvm/Support/InstVisitor.h" -#include "llvm/Target/TargetData.h" -#include "Support/CommandLine.h" -#include "Support/Debug.h" -#include "Support/Timer.h" - -// FIXME: This should eventually be a FunctionPass that is automatically -// aggregated into a Pass. -// -#include "llvm/Module.h" - -static RegisterAnalysis<LocalDataStructures> -X("datastructure", "Local Data Structure Analysis"); - -namespace DS { - // FIXME: Do something smarter with target data! - TargetData TD("temp-td"); - - // isPointerType - Return true if this type is big enough to hold a pointer. - bool isPointerType(const Type *Ty) { - if (isa<PointerType>(Ty)) - return true; - else if (Ty->isPrimitiveType() && Ty->isInteger()) - return Ty->getPrimitiveSize() >= PointerSize; - return false; - } -} -using namespace DS; - - -namespace { - cl::opt<bool> - DisableDirectCallOpt("disable-direct-call-dsopt", cl::Hidden, - cl::desc("Disable direct call optimization in " - "DSGraph construction")); - cl::opt<bool> - DisableFieldSensitivity("disable-ds-field-sensitivity", cl::Hidden, - cl::desc("Disable field sensitivity in DSGraphs")); - - //===--------------------------------------------------------------------===// - // GraphBuilder Class - //===--------------------------------------------------------------------===// - // - /// This class is the builder class that constructs the local data structure - /// graph by performing a single pass over the function in question. - /// - class GraphBuilder : InstVisitor<GraphBuilder> { - Function &F; - DSGraph &G; - DSNodeHandle &RetNode; // Node that gets returned... - DSGraph::ScalarMapTy &ScalarMap; - std::vector<DSCallSite> &FunctionCalls; - - public: - GraphBuilder(Function &f, DSGraph &g, DSNodeHandle &retNode, - DSGraph::ScalarMapTy &SM, std::vector<DSCallSite> &fc) - : F(f), G(g), RetNode(retNode), ScalarMap(SM), - FunctionCalls(fc) { - - // Create scalar nodes for all pointer arguments... - for (Function::aiterator I = F.abegin(), E = F.aend(); I != E; ++I) - if (isPointerType(I->getType())) - getValueDest(*I); - - visit(F); // Single pass over the function - } - - private: - // Visitor functions, used to handle each instruction type we encounter... - friend class InstVisitor<GraphBuilder>; - void visitMallocInst(MallocInst &MI) { handleAlloc(MI, true); } - void visitAllocaInst(AllocaInst &AI) { handleAlloc(AI, false); } - void handleAlloc(AllocationInst &AI, bool isHeap); - - void visitPHINode(PHINode &PN); - - void visitGetElementPtrInst(User &GEP); - void visitReturnInst(ReturnInst &RI); - void visitLoadInst(LoadInst &LI); - void visitStoreInst(StoreInst &SI); - void visitCallInst(CallInst &CI); - void visitSetCondInst(SetCondInst &SCI) {} // SetEQ & friends are ignored - void visitFreeInst(FreeInst &FI); - void visitCastInst(CastInst &CI); - void visitInstruction(Instruction &I); - - private: - // Helper functions used to implement the visitation functions... - - /// createNode - Create a new DSNode, ensuring that it is properly added to - /// the graph. - /// - DSNode *createNode(const Type *Ty = 0) { - DSNode *N = new DSNode(Ty, &G); // Create the node - if (DisableFieldSensitivity) { - N->foldNodeCompletely(); - if (DSNode *FN = N->getForwardNode()) - N = FN; - } - return N; - } - - /// setDestTo - Set the ScalarMap entry for the specified value to point to - /// the specified destination. If the Value already points to a node, make - /// sure to merge the two destinations together. - /// - void setDestTo(Value &V, const DSNodeHandle &NH); - - /// getValueDest - Return the DSNode that the actual value points to. - /// - DSNodeHandle getValueDest(Value &V); - - /// getLink - This method is used to return the specified link in the - /// specified node if one exists. If a link does not already exist (it's - /// null), then we create a new node, link it, then return it. - /// - DSNodeHandle &getLink(const DSNodeHandle &Node, unsigned Link = 0); - }; -} - -//===----------------------------------------------------------------------===// -// DSGraph constructor - Simply use the GraphBuilder to construct the local -// graph. -DSGraph::DSGraph(Function &F, DSGraph *GG) : GlobalsGraph(GG) { - PrintAuxCalls = false; - - DEBUG(std::cerr << " [Loc] Calculating graph for: " << F.getName() << "\n"); - - // Use the graph builder to construct the local version of the graph - GraphBuilder B(F, *this, ReturnNodes[&F], ScalarMap, FunctionCalls); -#ifndef NDEBUG - Timer::addPeakMemoryMeasurement(); -#endif - - // Remove all integral constants from the scalarmap! - for (ScalarMapTy::iterator I = ScalarMap.begin(); I != ScalarMap.end();) - if (isa<ConstantIntegral>(I->first)) { - ScalarMapTy::iterator J = I++; - ScalarMap.erase(J); - } else - ++I; - - markIncompleteNodes(DSGraph::MarkFormalArgs); - - // Remove any nodes made dead due to merging... - removeDeadNodes(DSGraph::KeepUnreachableGlobals); -} - - -//===----------------------------------------------------------------------===// -// Helper method implementations... -// - -/// getValueDest - Return the DSNode that the actual value points to. -/// -DSNodeHandle GraphBuilder::getValueDest(Value &Val) { - Value *V = &Val; - if (V == Constant::getNullValue(V->getType())) - return 0; // Null doesn't point to anything, don't add to ScalarMap! - - DSNodeHandle &NH = ScalarMap[V]; - if (NH.getNode()) - return NH; // Already have a node? Just return it... - - // Otherwise we need to create a new node to point to. - // Check first for constant expressions that must be traversed to - // extract the actual value. - if (Constant *C = dyn_cast<Constant>(V)) - if (ConstantPointerRef *CPR = dyn_cast<ConstantPointerRef>(C)) { - return NH = getValueDest(*CPR->getValue()); - } else if (ConstantExpr *CE = dyn_cast<ConstantExpr>(C)) { - if (CE->getOpcode() == Instruction::Cast) - NH = getValueDest(*CE->getOperand(0)); - else if (CE->getOpcode() == Instruction::GetElementPtr) { - visitGetElementPtrInst(*CE); - DSGraph::ScalarMapTy::iterator I = ScalarMap.find(CE); - assert(I != ScalarMap.end() && "GEP didn't get processed right?"); - NH = I->second; - } else { - // This returns a conservative unknown node for any unhandled ConstExpr - return NH = createNode()->setUnknownNodeMarker(); - } - if (NH.getNode() == 0) { // (getelementptr null, X) returns null - ScalarMap.erase(V); - return 0; - } - return NH; - - } else if (ConstantIntegral *CI = dyn_cast<ConstantIntegral>(C)) { - // Random constants are unknown mem - return NH = createNode()->setUnknownNodeMarker(); - } else { - assert(0 && "Unknown constant type!"); - } - - // Otherwise we need to create a new node to point to... - DSNode *N; - if (GlobalValue *GV = dyn_cast<GlobalValue>(V)) { - // Create a new global node for this global variable... - N = createNode(GV->getType()->getElementType()); - N->addGlobal(GV); - } else { - // Otherwise just create a shadow node - N = createNode(); - } - - NH.setNode(N); // Remember that we are pointing to it... - NH.setOffset(0); - return NH; -} - - -/// getLink - This method is used to return the specified link in the -/// specified node if one exists. If a link does not already exist (it's -/// null), then we create a new node, link it, then return it. We must -/// specify the type of the Node field we are accessing so that we know what -/// type should be linked to if we need to create a new node. -/// -DSNodeHandle &GraphBuilder::getLink(const DSNodeHandle &node, unsigned LinkNo) { - DSNodeHandle &Node = const_cast<DSNodeHandle&>(node); - DSNodeHandle &Link = Node.getLink(LinkNo); - if (!Link.getNode()) { - // If the link hasn't been created yet, make and return a new shadow node - Link = createNode(); - } - return Link; -} - - -/// setDestTo - Set the ScalarMap entry for the specified value to point to the -/// specified destination. If the Value already points to a node, make sure to -/// merge the two destinations together. -/// -void GraphBuilder::setDestTo(Value &V, const DSNodeHandle &NH) { - DSNodeHandle &AINH = ScalarMap[&V]; - if (AINH.getNode() == 0) // Not pointing to anything yet? - AINH = NH; // Just point directly to NH - else - AINH.mergeWith(NH); -} - - -//===----------------------------------------------------------------------===// -// Specific instruction type handler implementations... -// - -/// Alloca & Malloc instruction implementation - Simply create a new memory -/// object, pointing the scalar to it. -/// -void GraphBuilder::handleAlloc(AllocationInst &AI, bool isHeap) { - DSNode *N = createNode(); - if (isHeap) - N->setHeapNodeMarker(); - else - N->setAllocaNodeMarker(); - setDestTo(AI, N); -} - -// PHINode - Make the scalar for the PHI node point to all of the things the -// incoming values point to... which effectively causes them to be merged. -// -void GraphBuilder::visitPHINode(PHINode &PN) { - if (!isPointerType(PN.getType())) return; // Only pointer PHIs - - DSNodeHandle &PNDest = ScalarMap[&PN]; - for (unsigned i = 0, e = PN.getNumIncomingValues(); i != e; ++i) - PNDest.mergeWith(getValueDest(*PN.getIncomingValue(i))); -} - -void GraphBuilder::visitGetElementPtrInst(User &GEP) { - DSNodeHandle Value = getValueDest(*GEP.getOperand(0)); - if (Value.getNode() == 0) return; - - unsigned Offset = 0; - const PointerType *PTy = cast<PointerType>(GEP.getOperand(0)->getType()); - const Type *CurTy = PTy->getElementType(); - - if (Value.getNode()->mergeTypeInfo(CurTy, Value.getOffset())) { - // If the node had to be folded... exit quickly - setDestTo(GEP, Value); // GEP result points to folded node - return; - } - -#if 0 - // Handle the pointer index specially... - if (GEP.getNumOperands() > 1 && - GEP.getOperand(1) != ConstantSInt::getNullValue(Type::LongTy)) { - - // If we already know this is an array being accessed, don't do anything... - if (!TopTypeRec.isArray) { - TopTypeRec.isArray = true; - - // If we are treating some inner field pointer as an array, fold the node - // up because we cannot handle it right. This can come because of - // something like this: &((&Pt->X)[1]) == &Pt->Y - // - if (Value.getOffset()) { - // Value is now the pointer we want to GEP to be... - Value.getNode()->foldNodeCompletely(); - setDestTo(GEP, Value); // GEP result points to folded node - return; - } else { - // This is a pointer to the first byte of the node. Make sure that we - // are pointing to the outter most type in the node. - // FIXME: We need to check one more case here... - } - } - } -#endif - - // All of these subscripts are indexing INTO the elements we have... - for (unsigned i = 2, e = GEP.getNumOperands(); i < e; ++i) - if (GEP.getOperand(i)->getType() == Type::LongTy) { - // Get the type indexing into... - const SequentialType *STy = cast<SequentialType>(CurTy); - CurTy = STy->getElementType(); -#if 0 - if (ConstantSInt *CS = dyn_cast<ConstantSInt>(GEP.getOperand(i))) { - Offset += CS->getValue()*TD.getTypeSize(CurTy); - } else { - // Variable index into a node. We must merge all of the elements of the - // sequential type here. - if (isa<PointerType>(STy)) - std::cerr << "Pointer indexing not handled yet!\n"; - else { - const ArrayType *ATy = cast<ArrayType>(STy); - unsigned ElSize = TD.getTypeSize(CurTy); - DSNode *N = Value.getNode(); - assert(N && "Value must have a node!"); - unsigned RawOffset = Offset+Value.getOffset(); - - // Loop over all of the elements of the array, merging them into the - // zero'th element. - for (unsigned i = 1, e = ATy->getNumElements(); i != e; ++i) - // Merge all of the byte components of this array element - for (unsigned j = 0; j != ElSize; ++j) - N->mergeIndexes(RawOffset+j, RawOffset+i*ElSize+j); - } - } -#endif - } else if (GEP.getOperand(i)->getType() == Type::UByteTy) { - unsigned FieldNo = cast<ConstantUInt>(GEP.getOperand(i))->getValue(); - const StructType *STy = cast<StructType>(CurTy); - Offset += TD.getStructLayout(STy)->MemberOffsets[FieldNo]; - CurTy = STy->getContainedType(FieldNo); - } - - // Add in the offset calculated... - Value.setOffset(Value.getOffset()+Offset); - - // Value is now the pointer we want to GEP to be... - setDestTo(GEP, Value); -} - -void GraphBuilder::visitLoadInst(LoadInst &LI) { - DSNodeHandle Ptr = getValueDest(*LI.getOperand(0)); - if (Ptr.getNode() == 0) return; - - // Make that the node is read from... - Ptr.getNode()->setReadMarker(); - - // Ensure a typerecord exists... - Ptr.getNode()->mergeTypeInfo(LI.getType(), Ptr.getOffset(), false); - - if (isPointerType(LI.getType())) - setDestTo(LI, getLink(Ptr)); -} - -void GraphBuilder::visitStoreInst(StoreInst &SI) { - const Type *StoredTy = SI.getOperand(0)->getType(); - DSNodeHandle Dest = getValueDest(*SI.getOperand(1)); - if (Dest.getNode() == 0) return; - - // Mark that the node is written to... - Dest.getNode()->setModifiedMarker(); - - // Ensure a typerecord exists... - Dest.getNode()->mergeTypeInfo(StoredTy, Dest.getOffset()); - - // Avoid adding edges from null, or processing non-"pointer" stores - if (isPointerType(StoredTy)) - Dest.addEdgeTo(getValueDest(*SI.getOperand(0))); -} - -void GraphBuilder::visitReturnInst(ReturnInst &RI) { - if (RI.getNumOperands() && isPointerType(RI.getOperand(0)->getType())) - RetNode.mergeWith(getValueDest(*RI.getOperand(0))); -} - -void GraphBuilder::visitCallInst(CallInst &CI) { - // Set up the return value... - DSNodeHandle RetVal; - if (isPointerType(CI.getType())) - RetVal = getValueDest(CI); - - DSNode *Callee = 0; - if (DisableDirectCallOpt || !isa<Function>(CI.getOperand(0))) - Callee = getValueDest(*CI.getOperand(0)).getNode(); - - std::vector<DSNodeHandle> Args; - Args.reserve(CI.getNumOperands()-1); - - // Calculate the arguments vector... - for (unsigned i = 1, e = CI.getNumOperands(); i != e; ++i) - if (isPointerType(CI.getOperand(i)->getType())) - Args.push_back(getValueDest(*CI.getOperand(i))); - - // Add a new function call entry... - if (Callee) - FunctionCalls.push_back(DSCallSite(CI, RetVal, Callee, Args)); - else - FunctionCalls.push_back(DSCallSite(CI, RetVal, - cast<Function>(CI.getOperand(0)), Args)); -} - -void GraphBuilder::visitFreeInst(FreeInst &FI) { - // Mark that the node is written to... - DSNode *N = getValueDest(*FI.getOperand(0)).getNode(); - N->setModifiedMarker(); - N->setHeapNodeMarker(); -} - -/// Handle casts... -void GraphBuilder::visitCastInst(CastInst &CI) { - if (isPointerType(CI.getType())) - if (isPointerType(CI.getOperand(0)->getType())) { - // Cast one pointer to the other, just act like a copy instruction - setDestTo(CI, getValueDest(*CI.getOperand(0))); - } else { - // Cast something (floating point, small integer) to a pointer. We need - // to track the fact that the node points to SOMETHING, just something we - // don't know about. Make an "Unknown" node. - // - setDestTo(CI, createNode()->setUnknownNodeMarker()); - } -} - - -// visitInstruction - For all other instruction types, if we have any arguments -// that are of pointer type, make them have unknown composition bits, and merge -// the nodes together. -void GraphBuilder::visitInstruction(Instruction &Inst) { - DSNodeHandle CurNode; - if (isPointerType(Inst.getType())) - CurNode = getValueDest(Inst); - for (User::op_iterator I = Inst.op_begin(), E = Inst.op_end(); I != E; ++I) - if (isPointerType((*I)->getType())) - CurNode.mergeWith(getValueDest(**I)); - - if (CurNode.getNode()) - CurNode.getNode()->setUnknownNodeMarker(); -} - - - -//===----------------------------------------------------------------------===// -// LocalDataStructures Implementation -//===----------------------------------------------------------------------===// - -bool LocalDataStructures::run(Module &M) { - GlobalsGraph = new DSGraph(); - - // Calculate all of the graphs... - for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) - if (!I->isExternal()) - DSInfo.insert(std::make_pair(I, new DSGraph(*I, GlobalsGraph))); - return false; -} - -// releaseMemory - If the pass pipeline is done with this pass, we can release -// our memory... here... -// -void LocalDataStructures::releaseMemory() { - for (hash_map<Function*, DSGraph*>::iterator I = DSInfo.begin(), - E = DSInfo.end(); I != E; ++I) { - I->second->getReturnNodes().erase(I->first); - if (I->second->getReturnNodes().empty()) - delete I->second; - } - - // Empty map so next time memory is released, data structures are not - // re-deleted. - DSInfo.clear(); - delete GlobalsGraph; - GlobalsGraph = 0; -}
diff --git a/poolalloc/lib/DSA/Makefile b/poolalloc/lib/DSA/Makefile deleted file mode 100644 index 3c4c7e2..0000000 --- a/poolalloc/lib/DSA/Makefile +++ /dev/null
@@ -1,6 +0,0 @@ -LEVEL = ../../.. -LIBRARYNAME = datastructure -BUILD_ARCHIVE = 1 - -include $(LEVEL)/Makefile.common -
diff --git a/poolalloc/lib/DSA/MemoryDepAnalysis.cpp b/poolalloc/lib/DSA/MemoryDepAnalysis.cpp deleted file mode 100644 index 598bff9..0000000 --- a/poolalloc/lib/DSA/MemoryDepAnalysis.cpp +++ /dev/null
@@ -1,491 +0,0 @@ -//===- MemoryDepAnalysis.cpp - Compute dep graph for memory ops --*-C++-*--===// -// -// This file implements a pass (MemoryDepAnalysis) that computes memory-based -// data dependences between instructions for each function in a module. -// Memory-based dependences occur due to load and store operations, but -// also the side-effects of call instructions. -// -// The result of this pass is a DependenceGraph for each function -// representing the memory-based data dependences between instructions. -//===----------------------------------------------------------------------===// - -#include "llvm/Analysis/MemoryDepAnalysis.h" -#include "llvm/Analysis/IPModRef.h" -#include "llvm/Analysis/DataStructure.h" -#include "llvm/Analysis/DSGraph.h" -#include "llvm/Module.h" -#include "llvm/Function.h" -#include "llvm/iMemory.h" -#include "llvm/iOther.h" -#include "llvm/Support/InstVisitor.h" -#include "llvm/Support/CFG.h" -#include "Support/TarjanSCCIterator.h" -#include "Support/Statistic.h" -#include "Support/STLExtras.h" -#include "Support/hash_map" -#include "Support/hash_set" -#include <iostream> - - -///-------------------------------------------------------------------------- -/// struct ModRefTable: -/// -/// A data structure that tracks ModRefInfo for instructions: -/// -- modRefMap is a map of Instruction* -> ModRefInfo for the instr. -/// -- definers is a vector of instructions that define any node -/// -- users is a vector of instructions that reference any node -/// -- numUsersBeforeDef is a vector indicating that the number of users -/// seen before definers[i] is numUsersBeforeDef[i]. -/// -/// numUsersBeforeDef[] effectively tells us the exact interleaving of -/// definers and users within the ModRefTable. -/// This is only maintained when constructing the table for one SCC, and -/// not copied over from one table to another since it is no longer useful. -///-------------------------------------------------------------------------- - -struct ModRefTable -{ - typedef hash_map<Instruction*, ModRefInfo> ModRefMap; - typedef ModRefMap::const_iterator const_map_iterator; - typedef ModRefMap:: iterator map_iterator; - typedef std::vector<Instruction*>::const_iterator const_ref_iterator; - typedef std::vector<Instruction*>:: iterator ref_iterator; - - ModRefMap modRefMap; - std::vector<Instruction*> definers; - std::vector<Instruction*> users; - std::vector<unsigned> numUsersBeforeDef; - - // Iterators to enumerate all the defining instructions - const_ref_iterator defsBegin() const { return definers.begin(); } - ref_iterator defsBegin() { return definers.begin(); } - const_ref_iterator defsEnd() const { return definers.end(); } - ref_iterator defsEnd() { return definers.end(); } - - // Iterators to enumerate all the user instructions - const_ref_iterator usersBegin() const { return users.begin(); } - ref_iterator usersBegin() { return users.begin(); } - const_ref_iterator usersEnd() const { return users.end(); } - ref_iterator usersEnd() { return users.end(); } - - // Iterator identifying the last user that was seen *before* a - // specified def. In particular, all users in the half-closed range - // [ usersBegin(), usersBeforeDef_End(defPtr) ) - // were seen *before* the specified def. All users in the half-closed range - // [ usersBeforeDef_End(defPtr), usersEnd() ) - // were seen *after* the specified def. - // - ref_iterator usersBeforeDef_End(const_ref_iterator defPtr) { - unsigned defIndex = (unsigned) (defPtr - defsBegin()); - assert(defIndex < numUsersBeforeDef.size()); - assert(usersBegin() + numUsersBeforeDef[defIndex] <= usersEnd()); - return usersBegin() + numUsersBeforeDef[defIndex]; - } - const_ref_iterator usersBeforeDef_End(const_ref_iterator defPtr) const { - return const_cast<ModRefTable*>(this)->usersBeforeDef_End(defPtr); - } - - // - // Modifier methods - // - void AddDef(Instruction* D) { - definers.push_back(D); - numUsersBeforeDef.push_back(users.size()); - } - void AddUse(Instruction* U) { - users.push_back(U); - } - void Insert(const ModRefTable& fromTable) { - modRefMap.insert(fromTable.modRefMap.begin(), fromTable.modRefMap.end()); - definers.insert(definers.end(), - fromTable.definers.begin(), fromTable.definers.end()); - users.insert(users.end(), - fromTable.users.begin(), fromTable.users.end()); - numUsersBeforeDef.clear(); /* fromTable.numUsersBeforeDef is ignored */ - } -}; - - -///-------------------------------------------------------------------------- -/// class ModRefInfoBuilder: -/// -/// A simple InstVisitor<> class that retrieves the Mod/Ref info for -/// Load/Store/Call instructions and inserts this information in -/// a ModRefTable. It also records all instructions that Mod any node -/// and all that use any node. -///-------------------------------------------------------------------------- - -class ModRefInfoBuilder : public InstVisitor<ModRefInfoBuilder> { - const DSGraph& funcGraph; - const FunctionModRefInfo& funcModRef; - ModRefTable& modRefTable; - - ModRefInfoBuilder(); // DO NOT IMPLEMENT - ModRefInfoBuilder(const ModRefInfoBuilder&); // DO NOT IMPLEMENT - void operator=(const ModRefInfoBuilder&); // DO NOT IMPLEMENT - -public: - /*ctor*/ ModRefInfoBuilder(const DSGraph& _funcGraph, - const FunctionModRefInfo& _funcModRef, - ModRefTable& _modRefTable) - : funcGraph(_funcGraph), funcModRef(_funcModRef), modRefTable(_modRefTable) - { - } - - // At a call instruction, retrieve the ModRefInfo using IPModRef results. - // Add the call to the defs list if it modifies any nodes and to the uses - // list if it refs any nodes. - // - void visitCallInst (CallInst& callInst) { - ModRefInfo safeModRef(funcGraph.getGraphSize()); - const ModRefInfo* callModRef = funcModRef.getModRefInfo(callInst); - if (callModRef == NULL) - { // call to external/unknown function: mark all nodes as Mod and Ref - safeModRef.getModSet().set(); - safeModRef.getRefSet().set(); - callModRef = &safeModRef; - } - - modRefTable.modRefMap.insert(std::make_pair(&callInst, - ModRefInfo(*callModRef))); - if (callModRef->getModSet().any()) - modRefTable.AddDef(&callInst); - if (callModRef->getRefSet().any()) - modRefTable.AddUse(&callInst); - } - - // At a store instruction, add to the mod set the single node pointed to - // by the pointer argument of the store. Interestingly, if there is no - // such node, that would be a null pointer reference! - void visitStoreInst (StoreInst& storeInst) { - const DSNodeHandle& ptrNode = - funcGraph.getNodeForValue(storeInst.getPointerOperand()); - if (const DSNode* target = ptrNode.getNode()) - { - unsigned nodeId = funcModRef.getNodeId(target); - ModRefInfo& minfo = - modRefTable.modRefMap.insert( - std::make_pair(&storeInst, - ModRefInfo(funcGraph.getGraphSize()))).first->second; - minfo.setNodeIsMod(nodeId); - modRefTable.AddDef(&storeInst); - } - else - std::cerr << "Warning: Uninitialized pointer reference!\n"; - } - - // At a load instruction, add to the ref set the single node pointed to - // by the pointer argument of the load. Interestingly, if there is no - // such node, that would be a null pointer reference! - void visitLoadInst (LoadInst& loadInst) { - const DSNodeHandle& ptrNode = - funcGraph.getNodeForValue(loadInst.getPointerOperand()); - if (const DSNode* target = ptrNode.getNode()) - { - unsigned nodeId = funcModRef.getNodeId(target); - ModRefInfo& minfo = - modRefTable.modRefMap.insert( - std::make_pair(&loadInst, - ModRefInfo(funcGraph.getGraphSize()))).first->second; - minfo.setNodeIsRef(nodeId); - modRefTable.AddUse(&loadInst); - } - else - std::cerr << "Warning: Uninitialized pointer reference!\n"; - } -}; - - -//---------------------------------------------------------------------------- -// class MemoryDepAnalysis: A dep. graph for load/store/call instructions -//---------------------------------------------------------------------------- - -/// Basic dependence gathering algorithm, using TarjanSCCIterator on CFG: -/// -/// for every SCC S in the CFG in PostOrder on the SCC DAG -/// { -/// for every basic block BB in S in *postorder* -/// for every instruction I in BB in reverse -/// Add (I, ModRef[I]) to ModRefCurrent -/// if (Mod[I] != NULL) -/// Add I to DefSetCurrent: { I \in S : Mod[I] != NULL } -/// if (Ref[I] != NULL) -/// Add I to UseSetCurrent: { I : Ref[I] != NULL } -/// -/// for every def D in DefSetCurrent -/// -/// // NOTE: D comes after itself iff S contains a loop -/// if (HasLoop(S) && D & D) -/// Add output-dep: D -> D2 -/// -/// for every def D2 *after* D in DefSetCurrent -/// // NOTE: D2 comes before D in execution order -/// if (D & D2) -/// Add output-dep: D2 -> D -/// if (HasLoop(S)) -/// Add output-dep: D -> D2 -/// -/// for every use U in UseSetCurrent that was seen *before* D -/// // NOTE: U comes after D in execution order -/// if (U & D) -/// if (U != D || HasLoop(S)) -/// Add true-dep: D -> U -/// if (HasLoop(S)) -/// Add anti-dep: U -> D -/// -/// for every use U in UseSetCurrent that was seen *after* D -/// // NOTE: U comes before D in execution order -/// if (U & D) -/// if (U != D || HasLoop(S)) -/// Add anti-dep: U -> D -/// if (HasLoop(S)) -/// Add true-dep: D -> U -/// -/// for every def Dnext in DefSetAfter -/// // NOTE: Dnext comes after D in execution order -/// if (Dnext & D) -/// Add output-dep: D -> Dnext -/// -/// for every use Unext in UseSetAfter -/// // NOTE: Unext comes after D in execution order -/// if (Unext & D) -/// Add true-dep: D -> Unext -/// -/// for every use U in UseSetCurrent -/// for every def Dnext in DefSetAfter -/// // NOTE: Dnext comes after U in execution order -/// if (Dnext & D) -/// Add anti-dep: U -> Dnext -/// -/// Add ModRefCurrent to ModRefAfter: { (I, ModRef[I] ) } -/// Add DefSetCurrent to DefSetAfter: { I : Mod[I] != NULL } -/// Add UseSetCurrent to UseSetAfter: { I : Ref[I] != NULL } -/// } -/// -/// - -void MemoryDepAnalysis::ProcessSCC(SCC<Function*>& S, - ModRefTable& ModRefAfter) -{ - ModRefTable ModRefCurrent; - ModRefTable::ModRefMap& mapCurrent = ModRefCurrent.modRefMap; - ModRefTable::ModRefMap& mapAfter = ModRefAfter.modRefMap; - - bool hasLoop = S.HasLoop(); - - // Builder class fills out a ModRefTable one instruction at a time. - // To use it, we just invoke it's visit function for each basic block: - // - // for each basic block BB in the SCC in *postorder* - // for each instruction I in BB in *reverse* - // ModRefInfoBuilder::visit(I) - // : Add (I, ModRef[I]) to ModRefCurrent.modRefMap - // : Add I to ModRefCurrent.definers if it defines any node - // : Add I to ModRefCurrent.users if it uses any node - // - ModRefInfoBuilder builder(*funcGraph, *funcModRef, ModRefCurrent); - for (SCC<Function*>::iterator BI=S.begin(), BE=S.end(); BI != BE; ++BI) - // Note: BBs in the SCC<> created by TarjanSCCIterator are in postorder. - for (BasicBlock::reverse_iterator II=(*BI)->rbegin(), IE=(*BI)->rend(); - II != IE; ++II) - builder.visit(*II); - - /// for every def D in DefSetCurrent - /// - for (ModRefTable::ref_iterator II=ModRefCurrent.defsBegin(), - IE=ModRefCurrent.defsEnd(); II != IE; ++II) - { - /// // NOTE: D comes after itself iff S contains a loop - /// if (HasLoop(S)) - /// Add output-dep: D -> D2 - if (hasLoop) - funcDepGraph->AddSimpleDependence(**II, **II, OutputDependence); - - /// for every def D2 *after* D in DefSetCurrent - /// // NOTE: D2 comes before D in execution order - /// if (D2 & D) - /// Add output-dep: D2 -> D - /// if (HasLoop(S)) - /// Add output-dep: D -> D2 - for (ModRefTable::ref_iterator JI=II+1; JI != IE; ++JI) - if (!Disjoint(mapCurrent.find(*II)->second.getModSet(), - mapCurrent.find(*JI)->second.getModSet())) - { - funcDepGraph->AddSimpleDependence(**JI, **II, OutputDependence); - if (hasLoop) - funcDepGraph->AddSimpleDependence(**II, **JI, OutputDependence); - } - - /// for every use U in UseSetCurrent that was seen *before* D - /// // NOTE: U comes after D in execution order - /// if (U & D) - /// if (U != D || HasLoop(S)) - /// Add true-dep: U -> D - /// if (HasLoop(S)) - /// Add anti-dep: D -> U - ModRefTable::ref_iterator JI=ModRefCurrent.usersBegin(); - ModRefTable::ref_iterator JE = ModRefCurrent.usersBeforeDef_End(II); - for ( ; JI != JE; ++JI) - if (!Disjoint(mapCurrent.find(*II)->second.getModSet(), - mapCurrent.find(*JI)->second.getRefSet())) - { - if (*II != *JI || hasLoop) - funcDepGraph->AddSimpleDependence(**II, **JI, TrueDependence); - if (hasLoop) - funcDepGraph->AddSimpleDependence(**JI, **II, AntiDependence); - } - - /// for every use U in UseSetCurrent that was seen *after* D - /// // NOTE: U comes before D in execution order - /// if (U & D) - /// if (U != D || HasLoop(S)) - /// Add anti-dep: U -> D - /// if (HasLoop(S)) - /// Add true-dep: D -> U - for (/*continue JI*/ JE = ModRefCurrent.usersEnd(); JI != JE; ++JI) - if (!Disjoint(mapCurrent.find(*II)->second.getModSet(), - mapCurrent.find(*JI)->second.getRefSet())) - { - if (*II != *JI || hasLoop) - funcDepGraph->AddSimpleDependence(**JI, **II, AntiDependence); - if (hasLoop) - funcDepGraph->AddSimpleDependence(**II, **JI, TrueDependence); - } - - /// for every def Dnext in DefSetPrev - /// // NOTE: Dnext comes after D in execution order - /// if (Dnext & D) - /// Add output-dep: D -> Dnext - for (ModRefTable::ref_iterator JI=ModRefAfter.defsBegin(), - JE=ModRefAfter.defsEnd(); JI != JE; ++JI) - if (!Disjoint(mapCurrent.find(*II)->second.getModSet(), - mapAfter.find(*JI)->second.getModSet())) - funcDepGraph->AddSimpleDependence(**II, **JI, OutputDependence); - - /// for every use Unext in UseSetAfter - /// // NOTE: Unext comes after D in execution order - /// if (Unext & D) - /// Add true-dep: D -> Unext - for (ModRefTable::ref_iterator JI=ModRefAfter.usersBegin(), - JE=ModRefAfter.usersEnd(); JI != JE; ++JI) - if (!Disjoint(mapCurrent.find(*II)->second.getModSet(), - mapAfter.find(*JI)->second.getRefSet())) - funcDepGraph->AddSimpleDependence(**II, **JI, TrueDependence); - } - - /// - /// for every use U in UseSetCurrent - /// for every def Dnext in DefSetAfter - /// // NOTE: Dnext comes after U in execution order - /// if (Dnext & D) - /// Add anti-dep: U -> Dnext - for (ModRefTable::ref_iterator II=ModRefCurrent.usersBegin(), - IE=ModRefCurrent.usersEnd(); II != IE; ++II) - for (ModRefTable::ref_iterator JI=ModRefAfter.defsBegin(), - JE=ModRefAfter.defsEnd(); JI != JE; ++JI) - if (!Disjoint(mapCurrent.find(*II)->second.getRefSet(), - mapAfter.find(*JI)->second.getModSet())) - funcDepGraph->AddSimpleDependence(**II, **JI, AntiDependence); - - /// Add ModRefCurrent to ModRefAfter: { (I, ModRef[I] ) } - /// Add DefSetCurrent to DefSetAfter: { I : Mod[I] != NULL } - /// Add UseSetCurrent to UseSetAfter: { I : Ref[I] != NULL } - ModRefAfter.Insert(ModRefCurrent); -} - - -/// Debugging support methods -/// -void MemoryDepAnalysis::print(std::ostream &O) const -{ - // TEMPORARY LOOP - for (hash_map<Function*, DependenceGraph*>::const_iterator - I = funcMap.begin(), E = funcMap.end(); I != E; ++I) - { - Function* func = I->first; - DependenceGraph* depGraph = I->second; - - O << "\n================================================================\n"; - O << "DEPENDENCE GRAPH FOR MEMORY OPERATIONS IN FUNCTION " << func->getName(); - O << "\n================================================================\n\n"; - depGraph->print(*func, O); - - } -} - - -/// -/// Run the pass on a function -/// -bool MemoryDepAnalysis::runOnFunction(Function& func) -{ - assert(! func.isExternal()); - - // Get the FunctionModRefInfo holding IPModRef results for this function. - // Use the TD graph recorded within the FunctionModRefInfo object, which - // may not be the same as the original TD graph computed by DS analysis. - // - funcModRef = &getAnalysis<IPModRef>().getFunctionModRefInfo(func); - funcGraph = &funcModRef->getFuncGraph(); - - // TEMPORARY: ptr to depGraph (later just becomes "this"). - assert(funcMap.find(&func) == funcMap.end() && "Analyzing function twice?"); - funcDepGraph = funcMap[&func] = new DependenceGraph(); - - ModRefTable ModRefAfter; - - SCC<Function*>* nextSCC; - for (TarjanSCC_iterator<Function*> tarjSCCiter = tarj_begin(&func); - (nextSCC = *tarjSCCiter) != NULL; ++tarjSCCiter) - ProcessSCC(*nextSCC, ModRefAfter); - - return true; -} - - -//------------------------------------------------------------------------- -// TEMPORARY FUNCTIONS TO MAKE THIS A MODULE PASS --- -// These functions will go away once this class becomes a FunctionPass. -// - -// Driver function to compute dependence graphs for every function. -// This is temporary and will go away once this is a FunctionPass. -// -bool MemoryDepAnalysis::run(Module& M) -{ - for (Module::iterator FI = M.begin(), FE = M.end(); FI != FE; ++FI) - if (! FI->isExternal()) - runOnFunction(*FI); // automatically inserts each depGraph into funcMap - return true; -} - -// Release all the dependence graphs in the map. -void MemoryDepAnalysis::releaseMemory() -{ - for (hash_map<Function*, DependenceGraph*>::const_iterator - I = funcMap.begin(), E = funcMap.end(); I != E; ++I) - delete I->second; - funcMap.clear(); - - // Clear pointers because the pass constructor will not be invoked again. - funcDepGraph = NULL; - funcGraph = NULL; - funcModRef = NULL; -} - -MemoryDepAnalysis::~MemoryDepAnalysis() -{ - releaseMemory(); -} - -//----END TEMPORARY FUNCTIONS---------------------------------------------- - - -void MemoryDepAnalysis::dump() const -{ - this->print(std::cerr); -} - -static RegisterAnalysis<MemoryDepAnalysis> -Z("memdep", "Memory Dependence Analysis"); -
diff --git a/poolalloc/lib/DSA/Parallelize.cpp b/poolalloc/lib/DSA/Parallelize.cpp deleted file mode 100644 index 81a5252..0000000 --- a/poolalloc/lib/DSA/Parallelize.cpp +++ /dev/null
@@ -1,548 +0,0 @@ -//===- Parallelize.cpp - Auto parallelization using DS Graphs ---*- C++ -*-===// -// -// This file implements a pass that automatically parallelizes a program, -// using the Cilk multi-threaded runtime system to execute parallel code. -// -// The pass uses the Program Dependence Graph (class PDGIterator) to -// identify parallelizable function calls, i.e., calls whose instances -// can be executed in parallel with instances of other function calls. -// (In the future, this should also execute different instances of the same -// function call in parallel, but that requires parallelizing across -// loop iterations.) -// -// The output of the pass is LLVM code with: -// (1) all parallelizable functions renamed to flag them as parallelizable; -// (2) calls to a sync() function introduced at synchronization points. -// The CWriter recognizes these functions and inserts the appropriate Cilk -// keywords when writing out C code. This C code must be compiled with cilk2c. -// -// Current algorithmic limitations: -// -- no array dependence analysis -// -- no parallelization for function calls in different loop iterations -// (except in unlikely trivial cases) -// -// Limitations of using Cilk: -// -- No parallelism within a function body, e.g., in a loop; -// -- Simplistic synchronization model requiring all parallel threads -// created within a function to block at a sync(). -// -- Excessive overhead at "spawned" function calls, which has no benefit -// once all threads are busy (especially common when the degree of -// parallelism is low). -//===----------------------------------------------------------------------===// - - -#include "llvm/Transforms/Parallelize.h" -#include "llvm/Transforms/Utils/DemoteRegToStack.h" -#include "llvm/Analysis/PgmDependenceGraph.h" -#include "llvm/Analysis/Dominators.h" -#include "llvm/Analysis/DataStructure.h" -#include "llvm/Analysis/DSGraph.h" -#include "llvm/Module.h" -#include "llvm/Function.h" -#include "llvm/iOther.h" -#include "llvm/iPHINode.h" -#include "llvm/iTerminators.h" -#include "llvm/DerivedTypes.h" -#include "llvm/Support/InstVisitor.h" -#include "llvm/Support/Cilkifier.h" -#include "Support/Statistic.h" -#include "Support/STLExtras.h" -#include "Support/hash_set" -#include "Support/hash_map" -#include <vector> -#include <stack> -#include <functional> -#include <algorithm> - - - -#if 0 -void AddToDomSet(vector<BasicBlock*>& domSet, BasicBlock* bb, - const DominatorTree& domTree) -{ - DominatorTreeBase::Node* bbNode = domTree.getNode(bb); - const std::vector<Node*>& domKids = bbNode.getChildren(); - domSet.insert(domSet.end(), domKids.begin(), domKids.end()); - for (unsigned i = 0; i < domKids.size(); ++i) - AddToDomSet(domSet, domKids[i]->getNode(), domTree); -} - -bool CheckDominance(Function& func, - const CallInst& callInst1, - const CallInst& callInst2) -{ - if (callInst1 == callInst2) // makes sense if this is in a loop but - return false; // we're not handling loops yet - - // Check first if one call dominates the other - DominatorSet& domSet = getAnalysis<DominatorSet>(func); - if (domSet.dominates(callInst2, callInst1)) - { // swap callInst1 and callInst2 - const CallInst& tmp = callInst2; callInst2 = callInst1; callInst1 = tmp; - } - else if (! domSet.dominates(callInst1, callInst2)) - return false; // neither dominates the other: - - // - if (! AreIndependent(func, callInst1, callInst2)) - return false; -} - -#endif - - -//---------------------------------------------------------------------------- -// class Cilkifier -// -// Code generation pass that transforms code to identify where Cilk keywords -// should be inserted. This relies on dis -c to print out the keywords. -//---------------------------------------------------------------------------- - - -class Cilkifier: public InstVisitor<Cilkifier> -{ - Function* DummySyncFunc; - - // Data used when transforming each function. - hash_set<const Instruction*> stmtsVisited; // Flags for recursive DFS - hash_map<const CallInst*, hash_set<CallInst*> > spawnToSyncsMap; - - // Input data for the transformation. - const hash_set<Function*>* cilkFunctions; // Set of parallel functions - PgmDependenceGraph* depGraph; - - void DFSVisitInstr (Instruction* I, - Instruction* root, - hash_set<const Instruction*>& depsOfRoot); - -public: - /*ctor*/ Cilkifier (Module& M); - - // Transform a single function including its name, its call sites, and syncs - // - void TransformFunc (Function* F, - const hash_set<Function*>& cilkFunctions, - PgmDependenceGraph& _depGraph); - - // The visitor function that does most of the hard work, via DFSVisitInstr - // - void visitCallInst(CallInst& CI); -}; - - -Cilkifier::Cilkifier(Module& M) -{ - // create the dummy Sync function and add it to the Module - DummySyncFunc = new Function(FunctionType::get( Type::VoidTy, - std::vector<const Type*>(), - /*isVararg*/ false), - GlobalValue::ExternalLinkage, DummySyncFuncName, - &M); -} - -void Cilkifier::TransformFunc(Function* F, - const hash_set<Function*>& _cilkFunctions, - PgmDependenceGraph& _depGraph) -{ - // Memoize the information for this function - cilkFunctions = &_cilkFunctions; - depGraph = &_depGraph; - - // Add the marker suffix to the Function name - // This should automatically mark all calls to the function also! - F->setName(F->getName() + CilkSuffix); - - // Insert sync operations for each separate spawn - visit(*F); - - // Now traverse the CFG in rPostorder and eliminate redundant syncs, i.e., - // two consecutive sync's on a straight-line path with no intervening spawn. - -} - - -void Cilkifier::DFSVisitInstr(Instruction* I, - Instruction* root, - hash_set<const Instruction*>& depsOfRoot) -{ - assert(stmtsVisited.find(I) == stmtsVisited.end()); - stmtsVisited.insert(I); - - // If there is a dependence from root to I, insert Sync and return - if (depsOfRoot.find(I) != depsOfRoot.end()) - { // Insert a sync before I and stop searching along this path. - // If I is a Phi instruction, the dependence can only be an SSA dep. - // and we need to insert the sync in the predecessor on the appropriate - // incoming edge! - CallInst* syncI = 0; - if (PHINode* phiI = dyn_cast<PHINode>(I)) - { // check all operands of the Phi and insert before each one - for (unsigned i = 0, N = phiI->getNumIncomingValues(); i < N; ++i) - if (phiI->getIncomingValue(i) == root) - syncI = new CallInst(DummySyncFunc, std::vector<Value*>(), "", - phiI->getIncomingBlock(i)->getTerminator()); - } - else - syncI = new CallInst(DummySyncFunc, std::vector<Value*>(), "", I); - - // Remember the sync for each spawn to eliminate rendundant ones later - spawnToSyncsMap[cast<CallInst>(root)].insert(syncI); - - return; - } - - // else visit unvisited successors - if (BranchInst* brI = dyn_cast<BranchInst>(I)) - { // visit first instruction in each successor BB - for (unsigned i = 0, N = brI->getNumSuccessors(); i < N; ++i) - if (stmtsVisited.find(&brI->getSuccessor(i)->front()) - == stmtsVisited.end()) - DFSVisitInstr(&brI->getSuccessor(i)->front(), root, depsOfRoot); - } - else - if (Instruction* nextI = I->getNext()) - if (stmtsVisited.find(nextI) == stmtsVisited.end()) - DFSVisitInstr(nextI, root, depsOfRoot); -} - - -void Cilkifier::visitCallInst(CallInst& CI) -{ - assert(CI.getCalledFunction() != 0 && "Only direct calls can be spawned."); - if (cilkFunctions->find(CI.getCalledFunction()) == cilkFunctions->end()) - return; // not a spawn - - // Find all the outgoing memory dependences. - hash_set<const Instruction*> depsOfRoot; - for (PgmDependenceGraph::iterator DI = - depGraph->outDepBegin(CI, MemoryDeps); ! DI.fini(); ++DI) - depsOfRoot.insert(&DI->getSink()->getInstr()); - - // Now find all outgoing SSA dependences to the eventual non-Phi users of - // the call value (i.e., direct users that are not phis, and for any - // user that is a Phi, direct non-Phi users of that Phi, and recursively). - std::stack<const PHINode*> phiUsers; - hash_set<const PHINode*> phisSeen; // ensures we don't visit a phi twice - for (Value::use_iterator UI=CI.use_begin(), UE=CI.use_end(); UI != UE; ++UI) - if (const PHINode* phiUser = dyn_cast<PHINode>(*UI)) - { - if (phisSeen.find(phiUser) == phisSeen.end()) - { - phiUsers.push(phiUser); - phisSeen.insert(phiUser); - } - } - else - depsOfRoot.insert(cast<Instruction>(*UI)); - - // Now we've found the non-Phi users and immediate phi users. - // Recursively walk the phi users and add their non-phi users. - for (const PHINode* phiUser; !phiUsers.empty(); phiUsers.pop()) - { - phiUser = phiUsers.top(); - for (Value::use_const_iterator UI=phiUser->use_begin(), - UE=phiUser->use_end(); UI != UE; ++UI) - if (const PHINode* pn = dyn_cast<PHINode>(*UI)) - { - if (phisSeen.find(pn) == phisSeen.end()) - { - phiUsers.push(pn); - phisSeen.insert(pn); - } - } - else - depsOfRoot.insert(cast<Instruction>(*UI)); - } - - // Walk paths of the CFG starting at the call instruction and insert - // one sync before the first dependence on each path, if any. - if (! depsOfRoot.empty()) - { - stmtsVisited.clear(); // start a new DFS for this CallInst - assert(CI.getNext() && "Call instruction cannot be a terminator!"); - DFSVisitInstr(CI.getNext(), &CI, depsOfRoot); - } - - // Now, eliminate all users of the SSA value of the CallInst, i.e., - // if the call instruction returns a value, delete the return value - // register and replace it by a stack slot. - if (CI.getType() != Type::VoidTy) - DemoteRegToStack(CI); -} - - -//---------------------------------------------------------------------------- -// class FindParallelCalls -// -// Find all CallInst instructions that have at least one other CallInst -// that is independent. These are the instructions that can produce -// useful parallelism. -//---------------------------------------------------------------------------- - -class FindParallelCalls : public InstVisitor<FindParallelCalls> { - typedef hash_set<CallInst*> DependentsSet; - typedef DependentsSet::iterator Dependents_iterator; - typedef DependentsSet::const_iterator Dependents_const_iterator; - - PgmDependenceGraph& depGraph; // dependence graph for the function - hash_set<Instruction*> stmtsVisited; // flags for DFS walk of depGraph - hash_map<CallInst*, bool > completed; // flags marking if a CI is done - hash_map<CallInst*, DependentsSet> dependents; // dependent CIs for each CI - - void VisitOutEdges(Instruction* I, - CallInst* root, - DependentsSet& depsOfRoot); - - FindParallelCalls(const FindParallelCalls &); // DO NOT IMPLEMENT - void operator=(const FindParallelCalls&); // DO NOT IMPLEMENT -public: - std::vector<CallInst*> parallelCalls; - -public: - /*ctor*/ FindParallelCalls (Function& F, PgmDependenceGraph& DG); - void visitCallInst (CallInst& CI); -}; - - -FindParallelCalls::FindParallelCalls(Function& F, - PgmDependenceGraph& DG) - : depGraph(DG) -{ - // Find all CallInsts reachable from each CallInst using a recursive DFS - visit(F); - - // Now we've found all CallInsts reachable from each CallInst. - // Find those CallInsts that are parallel with at least one other CallInst - // by counting total inEdges and outEdges. - // - unsigned long totalNumCalls = completed.size(); - - if (totalNumCalls == 1) - { // Check first for the special case of a single call instruction not - // in any loop. It is not parallel, even if it has no dependences - // (this is why it is a special case). - // - // FIXME: - // THIS CASE IS NOT HANDLED RIGHT NOW, I.E., THERE IS NO - // PARALLELISM FOR CALLS IN DIFFERENT ITERATIONS OF A LOOP. - // - return; - } - - hash_map<CallInst*, unsigned long> numDeps; - for (hash_map<CallInst*, DependentsSet>::iterator II = dependents.begin(), - IE = dependents.end(); II != IE; ++II) - { - CallInst* fromCI = II->first; - numDeps[fromCI] += II->second.size(); - for (Dependents_iterator DI = II->second.begin(), DE = II->second.end(); - DI != DE; ++DI) - numDeps[*DI]++; // *DI can be reached from II->first - } - - for (hash_map<CallInst*, DependentsSet>::iterator - II = dependents.begin(), IE = dependents.end(); II != IE; ++II) - - // FIXME: Remove "- 1" when considering parallelism in loops - if (numDeps[II->first] < totalNumCalls - 1) - parallelCalls.push_back(II->first); -} - - -void FindParallelCalls::VisitOutEdges(Instruction* I, - CallInst* root, - DependentsSet& depsOfRoot) -{ - assert(stmtsVisited.find(I) == stmtsVisited.end() && "Stmt visited twice?"); - stmtsVisited.insert(I); - - if (CallInst* CI = dyn_cast<CallInst>(I)) - - // FIXME: Ignoring parallelism in a loop. Here we're actually *ignoring* - // a self-dependence in order to get the count comparison right above. - // When we include loop parallelism, self-dependences should be included. - // - if (CI != root) - - { // CallInst root has a path to CallInst I and any calls reachable from I - depsOfRoot.insert(CI); - if (completed[CI]) - { // We have already visited I so we know all nodes it can reach! - DependentsSet& depsOfI = dependents[CI]; - depsOfRoot.insert(depsOfI.begin(), depsOfI.end()); - return; - } - } - - // If we reach here, we need to visit all children of I - for (PgmDependenceGraph::iterator DI = depGraph.outDepBegin(*I); - ! DI.fini(); ++DI) - { - Instruction* sink = &DI->getSink()->getInstr(); - if (stmtsVisited.find(sink) == stmtsVisited.end()) - VisitOutEdges(sink, root, depsOfRoot); - } -} - - -void FindParallelCalls::visitCallInst(CallInst& CI) -{ - if (completed[&CI]) - return; - stmtsVisited.clear(); // clear flags to do a fresh DFS - - // Visit all children of CI using a recursive walk through dep graph - DependentsSet& depsOfRoot = dependents[&CI]; - for (PgmDependenceGraph::iterator DI = depGraph.outDepBegin(CI); - ! DI.fini(); ++DI) - { - Instruction* sink = &DI->getSink()->getInstr(); - if (stmtsVisited.find(sink) == stmtsVisited.end()) - VisitOutEdges(sink, &CI, depsOfRoot); - } - - completed[&CI] = true; -} - - -//---------------------------------------------------------------------------- -// class Parallelize -// -// (1) Find candidate parallel functions: any function F s.t. -// there is a call C1 to the function F that is followed or preceded -// by at least one other call C2 that is independent of this one -// (i.e., there is no dependence path from C1 to C2 or C2 to C1) -// (2) Label such a function F as a cilk function. -// (3) Convert every call to F to a spawn -// (4) For every function X, insert sync statements so that -// every spawn is postdominated by a sync before any statements -// with a data dependence to/from the call site for the spawn -// -//---------------------------------------------------------------------------- - -namespace { - class Parallelize: public Pass - { - public: - /// Driver functions to transform a program - /// - bool run(Module& M); - - /// getAnalysisUsage - Modifies extensively so preserve nothing. - /// Uses the DependenceGraph and the Top-down DS Graph (only to find - /// all functions called via an indirect call). - /// - void getAnalysisUsage(AnalysisUsage &AU) const { - AU.addRequired<TDDataStructures>(); - AU.addRequired<MemoryDepAnalysis>(); // force this not to be released - AU.addRequired<PgmDependenceGraph>(); // because it is needed by this - } - }; - - RegisterOpt<Parallelize> X("parallel", "Parallelize program using Cilk"); -} - - -static Function* FindMain(Module& M) -{ - for (Module::iterator FI = M.begin(), FE = M.end(); FI != FE; ++FI) - if (FI->getName() == std::string("main")) - return FI; - return NULL; -} - - -bool Parallelize::run(Module& M) -{ - hash_set<Function*> parallelFunctions; - hash_set<Function*> safeParallelFunctions; - hash_set<const GlobalValue*> indirectlyCalled; - - // If there is no main (i.e., for an incomplete program), we can do nothing. - // If there is a main, mark main as a parallel function. - // - Function* mainFunc = FindMain(M); - if (!mainFunc) - return false; - - // (1) Find candidate parallel functions and mark them as Cilk functions - // - for (Module::iterator FI = M.begin(), FE = M.end(); FI != FE; ++FI) - if (! FI->isExternal()) - { - Function* F = FI; - DSGraph& tdg = getAnalysis<TDDataStructures>().getDSGraph(*F); - - // All the hard analysis work gets done here! - // - FindParallelCalls finder(*F, - getAnalysis<PgmDependenceGraph>().getGraph(*F)); - /* getAnalysis<MemoryDepAnalysis>().getGraph(*F)); */ - - // Now we know which call instructions are useful to parallelize. - // Remember those callee functions. - // - for (std::vector<CallInst*>::iterator - CII = finder.parallelCalls.begin(), - CIE = finder.parallelCalls.end(); CII != CIE; ++CII) - { - // Check if this is a direct call... - if ((*CII)->getCalledFunction() != NULL) - { // direct call: if this is to a non-external function, - // mark it as a parallelizable function - if (! (*CII)->getCalledFunction()->isExternal()) - parallelFunctions.insert((*CII)->getCalledFunction()); - } - else - { // Indirect call: mark all potential callees as bad - std::vector<GlobalValue*> callees = - tdg.getNodeForValue((*CII)->getCalledValue()) - .getNode()->getGlobals(); - indirectlyCalled.insert(callees.begin(), callees.end()); - } - } - } - - // Remove all indirectly called functions from the list of Cilk functions. - // - for (hash_set<Function*>::iterator PFI = parallelFunctions.begin(), - PFE = parallelFunctions.end(); PFI != PFE; ++PFI) - if (indirectlyCalled.count(*PFI) == 0) - safeParallelFunctions.insert(*PFI); - -#undef CAN_USE_BIND1ST_ON_REFERENCE_TYPE_ARGS -#ifdef CAN_USE_BIND1ST_ON_REFERENCE_TYPE_ARGS - // Use this undecipherable STLese because erase invalidates iterators. - // Otherwise we have to copy sets as above. - hash_set<Function*>::iterator extrasBegin = - std::remove_if(parallelFunctions.begin(), parallelFunctions.end(), - compose1(std::bind2nd(std::greater<int>(), 0), - bind_obj(&indirectlyCalled, - &hash_set<const GlobalValue*>::count))); - parallelFunctions.erase(extrasBegin, parallelFunctions.end()); -#endif - - // If there are no parallel functions, we can just give up. - if (safeParallelFunctions.empty()) - return false; - - // Add main as a parallel function since Cilk requires this. - safeParallelFunctions.insert(mainFunc); - - // (2,3) Transform each Cilk function and all its calls simply by - // adding a unique suffix to the function name. - // This should identify both functions and calls to such functions - // to the code generator. - // (4) Also, insert calls to sync at appropriate points. - // - Cilkifier cilkifier(M); - for (hash_set<Function*>::iterator CFI = safeParallelFunctions.begin(), - CFE = safeParallelFunctions.end(); CFI != CFE; ++CFI) - { - cilkifier.TransformFunc(*CFI, safeParallelFunctions, - getAnalysis<PgmDependenceGraph>().getGraph(**CFI)); - /* getAnalysis<MemoryDepAnalysis>().getGraph(**CFI)); */ - } - - return true; -}
diff --git a/poolalloc/lib/DSA/PgmDependenceGraph.cpp b/poolalloc/lib/DSA/PgmDependenceGraph.cpp deleted file mode 100644 index 705a944..0000000 --- a/poolalloc/lib/DSA/PgmDependenceGraph.cpp +++ /dev/null
@@ -1,248 +0,0 @@ -//===- PgmDependenceGraph.cpp - Enumerate PDG for a function ----*- C++ -*-===// -// -// The Program Dependence Graph (PDG) for a single function represents all -// data and control dependences for the function. This file provides an -// iterator to enumerate all these dependences. In particular, it enumerates: -// -// -- Data dependences on memory locations, computed using the -// MemoryDepAnalysis pass; -// -- Data dependences on SSA registers, directly from Def-Use edges of Values; -// -- Control dependences, computed using postdominance frontiers -// (NOT YET IMPLEMENTED). -// -// Note that this file does not create an explicit dependence graph -- -// it only provides an iterator to traverse the PDG conceptually. -// The MemoryDepAnalysis does build an explicit graph, which is used internally -// here. That graph could be augmented with the other dependences above if -// desired, but for most uses there will be little need to do that. -// -//===----------------------------------------------------------------------===// - -#include "llvm/Analysis/PgmDependenceGraph.h" -#include "llvm/Analysis/MemoryDepAnalysis.h" -#include "llvm/Analysis/PostDominators.h" -#include "llvm/Function.h" - - -//---------------------------------------------------------------------------- -// class DepIterState -//---------------------------------------------------------------------------- - -const DepIterState::IterStateFlags DepIterState::NoFlag = 0x0; -const DepIterState::IterStateFlags DepIterState::MemDone = 0x1; -const DepIterState::IterStateFlags DepIterState::SSADone = 0x2; -const DepIterState::IterStateFlags DepIterState::AllDone = 0x4; -const DepIterState::IterStateFlags DepIterState::FirstTimeFlag= 0x8; - -// Find the first memory dependence for the current Mem In/Out iterators. -// Find the first memory dependence for the current Mem In/Out iterators. -// Sets dep to that dependence and returns true if one is found. -// -bool DepIterState::SetFirstMemoryDep() -{ - if (! (depFlags & MemoryDeps)) - return false; - - bool doIncomingDeps = dep.getDepType() & IncomingFlag; - - if (( doIncomingDeps && memDepIter == memDepGraph->inDepEnd( *depNode)) || - (!doIncomingDeps && memDepIter == memDepGraph->outDepEnd(*depNode))) - { - iterFlags |= MemDone; - return false; - } - - dep = *memDepIter; // simple copy from dependence in memory DepGraph - - return true; -} - - -// Find the first valid data dependence for the current SSA In/Out iterators. -// A valid data dependence is one that is to/from an Instruction. -// E.g., an SSA edge from a formal parameter is not a valid dependence. -// Sets dep to that dependence and returns true if a valid one is found. -// Returns false and leaves dep unchanged otherwise. -// -bool DepIterState::SetFirstSSADep() -{ - if (! (depFlags & SSADeps)) - return false; - - bool doIncomingDeps = dep.getDepType() & IncomingFlag; - Instruction* firstTarget = NULL; - - // Increment the In or Out iterator till it runs out or we find a valid dep - if (doIncomingDeps) - for (Instruction::op_iterator E = depNode->getInstr().op_end(); - ssaInEdgeIter != E && - (firstTarget = dyn_cast<Instruction>(ssaInEdgeIter))== NULL; ) - ++ssaInEdgeIter; - else - for (Value::use_iterator E = depNode->getInstr().use_end(); - ssaOutEdgeIter != E && - (firstTarget = dyn_cast<Instruction>(*ssaOutEdgeIter)) == NULL; ) - ++ssaOutEdgeIter; - - // If the iterator ran out before we found a valid dep, there isn't one. - if (!firstTarget) - { - iterFlags |= SSADone; - return false; - } - - // Create a simple dependence object to represent this SSA dependence. - dep = Dependence(memDepGraph->getNode(*firstTarget, /*create*/ true), - TrueDependence, doIncomingDeps); - - return true; -} - - -DepIterState::DepIterState(DependenceGraph* _memDepGraph, - Instruction& I, - bool incomingDeps, - PDGIteratorFlags whichDeps) - : memDepGraph(_memDepGraph), - depFlags(whichDeps), - iterFlags(NoFlag) -{ - depNode = memDepGraph->getNode(I, /*create*/ true); - - if (incomingDeps) - { - if (whichDeps & MemoryDeps) memDepIter= memDepGraph->inDepBegin(*depNode); - if (whichDeps & SSADeps) ssaInEdgeIter = I.op_begin(); - /* Initialize control dependence iterator here. */ - } - else - { - if (whichDeps & MemoryDeps) memDepIter=memDepGraph->outDepBegin(*depNode); - if (whichDeps & SSADeps) ssaOutEdgeIter = I.use_begin(); - /* Initialize control dependence iterator here. */ - } - - // Set the dependence to the first of a memory dep or an SSA dep - // and set the done flag if either is found. Otherwise, set the - // init flag to indicate that the iterators have just been initialized. - // - if (!SetFirstMemoryDep() && !SetFirstSSADep()) - iterFlags |= AllDone; - else - iterFlags |= FirstTimeFlag; -} - - -// Helper function for ++ operator that bumps iterator by 1 (to next -// dependence) and resets the dep field to represent the new dependence. -// -void DepIterState::Next() -{ - // firstMemDone and firstSsaDone are used to indicate when the memory or - // SSA iterators just ran out, or when this is the very first increment. - // In either case, the next iterator (if any) should not be incremented. - // - bool firstMemDone = iterFlags & FirstTimeFlag; - bool firstSsaDone = iterFlags & FirstTimeFlag; - bool doIncomingDeps = dep.getDepType() & IncomingFlag; - - if (depFlags & MemoryDeps && ! (iterFlags & MemDone)) - { - iterFlags &= ~FirstTimeFlag; // clear "firstTime" flag - ++memDepIter; - if (SetFirstMemoryDep()) - return; - firstMemDone = true; // flags that we _just_ rolled over - } - - if (depFlags & SSADeps && ! (iterFlags & SSADone)) - { - // Don't increment the SSA iterator if we either just rolled over from - // the memory dep iterator, or if the SSA iterator is already done. - iterFlags &= ~FirstTimeFlag; // clear "firstTime" flag - if (! firstMemDone) - if (doIncomingDeps) ++ssaInEdgeIter; - else ++ssaOutEdgeIter; - if (SetFirstSSADep()) - return; - firstSsaDone = true; // flags if we just rolled over - } - - if (depFlags & ControlDeps != 0) - { - assert(0 && "Cannot handle control deps"); - // iterFlags &= ~FirstTimeFlag; // clear "firstTime" flag - } - - // This iterator is now complete. - iterFlags |= AllDone; -} - - -//---------------------------------------------------------------------------- -// class PgmDependenceGraph -//---------------------------------------------------------------------------- - - -// MakeIterator -- Create and initialize an iterator as specified. -// -PDGIterator PgmDependenceGraph::MakeIterator(Instruction& I, - bool incomingDeps, - PDGIteratorFlags whichDeps) -{ - assert(memDepGraph && "Function not initialized!"); - return PDGIterator(new DepIterState(memDepGraph, I, incomingDeps, whichDeps)); -} - - -void PgmDependenceGraph::printOutgoingSSADeps(Instruction& I, - std::ostream &O) -{ - iterator SI = this->outDepBegin(I, SSADeps); - iterator SE = this->outDepEnd(I, SSADeps); - if (SI == SE) - return; - - O << "\n Outgoing SSA dependences:\n"; - for ( ; SI != SE; ++SI) - { - O << "\t"; - SI->print(O); - O << " to instruction:"; - O << SI->getSink()->getInstr(); - } -} - - -void PgmDependenceGraph::print(std::ostream &O) const -{ - MemoryDepAnalysis& graphSet = getAnalysis<MemoryDepAnalysis>(); - - // TEMPORARY LOOP - for (hash_map<Function*, DependenceGraph*>::iterator - I = graphSet.funcMap.begin(), E = graphSet.funcMap.end(); - I != E; ++I) - { - Function* func = I->first; - DependenceGraph* depGraph = I->second; - const_cast<PgmDependenceGraph*>(this)->runOnFunction(*func); - - O << "DEPENDENCE GRAPH FOR FUNCTION " << func->getName() << ":\n"; - for (Function::iterator BB=func->begin(), FE=func->end(); BB != FE; ++BB) - for (BasicBlock::iterator II=BB->begin(), IE=BB->end(); II !=IE; ++II) - { - DepGraphNode* dgNode = depGraph->getNode(*II, /*create*/ true); - dgNode->print(O); - const_cast<PgmDependenceGraph*>(this)->printOutgoingSSADeps(*II, O); - } - } // END TEMPORARY LOOP -} - - -void PgmDependenceGraph::dump() const -{ - this->print(std::cerr); -} - -static RegisterAnalysis<PgmDependenceGraph> -Z("pgmdep", "Enumerate Program Dependence Graph (data and control)");
diff --git a/poolalloc/lib/DSA/Printer.cpp b/poolalloc/lib/DSA/Printer.cpp deleted file mode 100644 index 87fc1f7..0000000 --- a/poolalloc/lib/DSA/Printer.cpp +++ /dev/null
@@ -1,275 +0,0 @@ -//===- Printer.cpp - Code for printing data structure graphs nicely -------===// -// -// This file implements the 'dot' graph printer. -// -//===----------------------------------------------------------------------===// - -#include "llvm/Analysis/DataStructure.h" -#include "llvm/Analysis/DSGraph.h" -#include "llvm/Analysis/DSGraphTraits.h" -#include "llvm/Module.h" -#include "llvm/Constants.h" -#include "llvm/Assembly/Writer.h" -#include "Support/CommandLine.h" -#include "Support/GraphWriter.h" -#include "Support/Statistic.h" -#include <fstream> -#include <sstream> - -// OnlyPrintMain - The DataStructure printer exposes this option to allow -// printing of only the graph for "main". -// -namespace { - cl::opt<bool> OnlyPrintMain("only-print-main-ds", cl::ReallyHidden); - Statistic<> MaxGraphSize ("dsnode", "Maximum graph size"); - Statistic<> NumFoldedNodes ("dsnode", "Number of folded nodes (in final graph)"); -} - - -void DSNode::dump() const { print(std::cerr, 0); } - -static std::string getCaption(const DSNode *N, const DSGraph *G) { - std::stringstream OS; - Module *M = 0; - // Get the module from ONE of the functions in the graph it is available. - if (G && !G->getReturnNodes().empty()) - M = G->getReturnNodes().begin()->first->getParent(); - - if (N->isNodeCompletelyFolded()) - OS << "COLLAPSED"; - else { - WriteTypeSymbolic(OS, N->getType(), M); - if (N->isArray()) - OS << " array"; - } - if (unsigned NodeType = N->getNodeFlags()) { - OS << ": "; - if (NodeType & DSNode::AllocaNode ) OS << "S"; - if (NodeType & DSNode::HeapNode ) OS << "H"; - if (NodeType & DSNode::GlobalNode ) OS << "G"; - if (NodeType & DSNode::UnknownNode) OS << "U"; - if (NodeType & DSNode::Incomplete ) OS << "I"; - if (NodeType & DSNode::Modified ) OS << "M"; - if (NodeType & DSNode::Read ) OS << "R"; -#ifndef NDEBUG - if (NodeType & DSNode::DEAD ) OS << "<dead>"; -#endif - OS << "\n"; - } - - for (unsigned i = 0, e = N->getGlobals().size(); i != e; ++i) { - WriteAsOperand(OS, N->getGlobals()[i], false, true, M); - OS << "\n"; - } - - return OS.str(); -} - -template<> -struct DOTGraphTraits<const DSGraph*> : public DefaultDOTGraphTraits { - static std::string getGraphName(const DSGraph *G) { - switch (G->getReturnNodes().size()) { - case 0: return G->getFunctionNames(); - case 1: return "Function " + G->getFunctionNames(); - default: return "Functions: " + G->getFunctionNames(); - } - } - - static const char *getGraphProperties(const DSGraph *G) { - return "\tsize=\"10,7.5\";\n" - "\trotate=\"90\";\n"; - } - - static std::string getNodeLabel(const DSNode *Node, const DSGraph *Graph) { - return getCaption(Node, Graph); - } - - static std::string getNodeAttributes(const DSNode *N) { - return "shape=Mrecord";//fontname=Courier"; - } - - /// addCustomGraphFeatures - Use this graph writing hook to emit call nodes - /// and the return node. - /// - static void addCustomGraphFeatures(const DSGraph *G, - GraphWriter<const DSGraph*> &GW) { - Module *CurMod = 0; - if (!G->getReturnNodes().empty()) - CurMod = G->getReturnNodes().begin()->first->getParent(); - - // Add scalar nodes to the graph... - const DSGraph::ScalarMapTy &VM = G->getScalarMap(); - for (DSGraph::ScalarMapTy::const_iterator I = VM.begin(); I != VM.end();++I) - if (!isa<GlobalValue>(I->first) && !isa<ConstantPointerRef>(I->first)) { - std::stringstream OS; - WriteAsOperand(OS, I->first, false, true, CurMod); - GW.emitSimpleNode(I->first, "", OS.str()); - - // Add edge from return node to real destination - int EdgeDest = I->second.getOffset() >> DS::PointerShift; - if (EdgeDest == 0) EdgeDest = -1; - GW.emitEdge(I->first, -1, I->second.getNode(), - EdgeDest, "arrowtail=tee,color=gray63"); - } - - - // Output the returned value pointer... - const DSGraph::ReturnNodesTy &RetNodes = G->getReturnNodes(); - for (DSGraph::ReturnNodesTy::const_iterator I = RetNodes.begin(), - E = RetNodes.end(); I != E; ++I) - if (I->second.getNode()) { - std::string Label; - if (RetNodes.size() == 1) - Label = "returning"; - else - Label = I->first->getName() + " ret node"; - // Output the return node... - GW.emitSimpleNode((void*)1, "plaintext=circle", Label); - - // Add edge from return node to real destination - int RetEdgeDest = I->second.getOffset() >> DS::PointerShift;; - if (RetEdgeDest == 0) RetEdgeDest = -1; - GW.emitEdge((void*)1, -1, I->second.getNode(), - RetEdgeDest, "arrowtail=tee,color=gray63"); - } - - // Output all of the call nodes... - const std::vector<DSCallSite> &FCs = - G->shouldPrintAuxCalls() ? G->getAuxFunctionCalls() - : G->getFunctionCalls(); - for (unsigned i = 0, e = FCs.size(); i != e; ++i) { - const DSCallSite &Call = FCs[i]; - std::vector<std::string> EdgeSourceCaptions(Call.getNumPtrArgs()+2); - EdgeSourceCaptions[0] = "r"; - if (Call.isDirectCall()) - EdgeSourceCaptions[1] = Call.getCalleeFunc()->getName(); - else - EdgeSourceCaptions[1] = "f"; - - GW.emitSimpleNode(&Call, "shape=record", "call", Call.getNumPtrArgs()+2, - &EdgeSourceCaptions); - - if (DSNode *N = Call.getRetVal().getNode()) { - int EdgeDest = Call.getRetVal().getOffset() >> DS::PointerShift; - if (EdgeDest == 0) EdgeDest = -1; - GW.emitEdge(&Call, 0, N, EdgeDest, "color=gray63,tailclip=false"); - } - - // Print out the callee... - if (Call.isIndirectCall()) { - DSNode *N = Call.getCalleeNode(); - assert(N && "Null call site callee node!"); - GW.emitEdge(&Call, 1, N, -1, "color=gray63,tailclip=false"); - } - - for (unsigned j = 0, e = Call.getNumPtrArgs(); j != e; ++j) - if (DSNode *N = Call.getPtrArg(j).getNode()) { - int EdgeDest = Call.getPtrArg(j).getOffset() >> DS::PointerShift; - if (EdgeDest == 0) EdgeDest = -1; - GW.emitEdge(&Call, j+2, N, EdgeDest, "color=gray63,tailclip=false"); - } - } - } -}; - -void DSNode::print(std::ostream &O, const DSGraph *G) const { - GraphWriter<const DSGraph *> W(O, G); - W.writeNode(this); -} - -void DSGraph::print(std::ostream &O) const { - WriteGraph(O, this, "DataStructures"); -} - -void DSGraph::writeGraphToFile(std::ostream &O, - const std::string &GraphName) const { - std::string Filename = GraphName + ".dot"; - O << "Writing '" << Filename << "'..."; - std::ofstream F(Filename.c_str()); - - if (F.good()) { - print(F); - unsigned NumCalls = shouldPrintAuxCalls() ? - getAuxFunctionCalls().size() : getFunctionCalls().size(); - O << " [" << getGraphSize() << "+" << NumCalls << "]\n"; - } else { - O << " error opening file for writing!\n"; - } -} - -/// viewGraph - Emit a dot graph, run 'dot', run gv on the postscript file, -/// then cleanup. For use from the debugger. -/// -void DSGraph::viewGraph() const { - std::ofstream F("/tmp/tempgraph.dot"); - if (!F.good()) { - std::cerr << "Error opening '/tmp/tempgraph.dot' for temporary graph!\n"; - return; - } - print(F); - F.close(); - if (system("dot -Tps /tmp/tempgraph.dot > /tmp/tempgraph.ps")) - std::cerr << "Error running dot: 'dot' not in path?\n"; - system("gv /tmp/tempgraph.ps"); - system("rm /tmp/tempgraph.dot /tmp/tempgraph.ps"); -} - - -template <typename Collection> -static void printCollection(const Collection &C, std::ostream &O, - const Module *M, const std::string &Prefix) { - if (M == 0) { - O << "Null Module pointer, cannot continue!\n"; - return; - } - - unsigned TotalNumNodes = 0, TotalCallNodes = 0; - for (Module::const_iterator I = M->begin(), E = M->end(); I != E; ++I) - if (C.hasGraph(*I)) { - DSGraph &Gr = C.getDSGraph((Function&)*I); - TotalNumNodes += Gr.getGraphSize(); - unsigned NumCalls = Gr.shouldPrintAuxCalls() ? - Gr.getAuxFunctionCalls().size() : Gr.getFunctionCalls().size(); - - TotalCallNodes += NumCalls; - if (I->getName() == "main" || !OnlyPrintMain) - Gr.writeGraphToFile(O, Prefix+I->getName()); - else { - O << "Skipped Writing '" << Prefix+I->getName() << ".dot'... [" - << Gr.getGraphSize() << "+" << NumCalls << "]\n"; - } - - if (MaxGraphSize < Gr.getNodes().size()) - MaxGraphSize = Gr.getNodes().size(); - for (unsigned i = 0, e = Gr.getNodes().size(); i != e; ++i) - if (Gr.getNodes()[i]->isNodeCompletelyFolded()) - ++NumFoldedNodes; - } - - DSGraph &GG = C.getGlobalsGraph(); - TotalNumNodes += GG.getGraphSize(); - TotalCallNodes += GG.getFunctionCalls().size(); - if (!OnlyPrintMain) { - GG.writeGraphToFile(O, Prefix+"GlobalsGraph"); - } else { - O << "Skipped Writing '" << Prefix << "GlobalsGraph.dot'... [" - << GG.getGraphSize() << "+" << GG.getFunctionCalls().size() << "]\n"; - } - - O << "\nGraphs contain [" << TotalNumNodes << "+" << TotalCallNodes - << "] nodes total" << std::endl; -} - - -// print - Print out the analysis results... -void LocalDataStructures::print(std::ostream &O, const Module *M) const { - printCollection(*this, O, M, "ds."); -} - -void BUDataStructures::print(std::ostream &O, const Module *M) const { - printCollection(*this, O, M, "bu."); -} - -void TDDataStructures::print(std::ostream &O, const Module *M) const { - printCollection(*this, O, M, "td."); -}
diff --git a/poolalloc/lib/DSA/Steensgaard.cpp b/poolalloc/lib/DSA/Steensgaard.cpp deleted file mode 100644 index a3034dd..0000000 --- a/poolalloc/lib/DSA/Steensgaard.cpp +++ /dev/null
@@ -1,230 +0,0 @@ -//===- Steensgaard.cpp - Context Insensitive Alias Analysis ---------------===// -// -// This pass uses the data structure graphs to implement a simple context -// insensitive alias analysis. It does this by computing the local analysis -// graphs for all of the functions, then merging them together into a single big -// graph without cloning. -// -//===----------------------------------------------------------------------===// - -#include "llvm/Analysis/DataStructure.h" -#include "llvm/Analysis/DSGraph.h" -#include "llvm/Analysis/AliasAnalysis.h" -#include "llvm/Module.h" -#include "Support/Debug.h" - -namespace { - class Steens : public Pass, public AliasAnalysis { - DSGraph *ResultGraph; - DSGraph *GlobalsGraph; // FIXME: Eliminate globals graph stuff from DNE - public: - Steens() : ResultGraph(0), GlobalsGraph(0) {} - ~Steens() { - releaseMyMemory(); - assert(ResultGraph == 0 && "releaseMemory not called?"); - } - - //------------------------------------------------ - // Implement the Pass API - // - - // run - Build up the result graph, representing the pointer graph for the - // program. - // - bool run(Module &M); - - virtual void releaseMyMemory() { delete ResultGraph; ResultGraph = 0; } - - virtual void getAnalysisUsage(AnalysisUsage &AU) const { - AliasAnalysis::getAnalysisUsage(AU); - AU.setPreservesAll(); // Does not transform code... - AU.addRequired<LocalDataStructures>(); // Uses local dsgraph - AU.addRequired<AliasAnalysis>(); // Chains to another AA impl... - } - - // print - Implement the Pass::print method... - void print(std::ostream &O, const Module *M) const { - assert(ResultGraph && "Result graph has not yet been computed!"); - ResultGraph->writeGraphToFile(O, "steensgaards"); - } - - //------------------------------------------------ - // Implement the AliasAnalysis API - // - - // alias - This is the only method here that does anything interesting... - AliasResult alias(const Value *V1, unsigned V1Size, - const Value *V2, unsigned V2Size); - - private: - void ResolveFunctionCall(Function *F, const DSCallSite &Call, - DSNodeHandle &RetVal); - }; - - // Register the pass... - RegisterOpt<Steens> X("steens-aa", - "Steensgaard's alias analysis (DSGraph based)"); - - // Register as an implementation of AliasAnalysis - RegisterAnalysisGroup<AliasAnalysis, Steens> Y; -} - - -/// ResolveFunctionCall - Resolve the actual arguments of a call to function F -/// with the specified call site descriptor. This function links the arguments -/// and the return value for the call site context-insensitively. -/// -void Steens::ResolveFunctionCall(Function *F, const DSCallSite &Call, - DSNodeHandle &RetVal) { - assert(ResultGraph != 0 && "Result graph not allocated!"); - DSGraph::ScalarMapTy &ValMap = ResultGraph->getScalarMap(); - - // Handle the return value of the function... - if (Call.getRetVal().getNode() && RetVal.getNode()) - RetVal.mergeWith(Call.getRetVal()); - - // Loop over all pointer arguments, resolving them to their provided pointers - unsigned PtrArgIdx = 0; - for (Function::aiterator AI = F->abegin(), AE = F->aend(); - AI != AE && PtrArgIdx < Call.getNumPtrArgs(); ++AI) { - DSGraph::ScalarMapTy::iterator I = ValMap.find(AI); - if (I != ValMap.end()) // If its a pointer argument... - I->second.mergeWith(Call.getPtrArg(PtrArgIdx++)); - } -} - - -/// run - Build up the result graph, representing the pointer graph for the -/// program. -/// -bool Steens::run(Module &M) { - InitializeAliasAnalysis(this); - assert(ResultGraph == 0 && "Result graph already allocated!"); - LocalDataStructures &LDS = getAnalysis<LocalDataStructures>(); - - // Create a new, empty, graph... - ResultGraph = new DSGraph(); - GlobalsGraph = new DSGraph(); - ResultGraph->setGlobalsGraph(GlobalsGraph); - ResultGraph->setPrintAuxCalls(); - - // RetValMap - Keep track of the return values for all functions that return - // valid pointers. - // - DSGraph::ReturnNodesTy RetValMap; - - // Loop over the rest of the module, merging graphs for non-external functions - // into this graph. - // - unsigned Count = 0; - for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) - if (!I->isExternal()) { - DSGraph::ScalarMapTy ValMap; - { // Scope to free NodeMap memory ASAP - DSGraph::NodeMapTy NodeMap; - const DSGraph &FDSG = LDS.getDSGraph(*I); - ResultGraph->cloneInto(FDSG, ValMap, RetValMap, NodeMap); - } - - // Incorporate the inlined Function's ScalarMap into the global - // ScalarMap... - DSGraph::ScalarMapTy &GVM = ResultGraph->getScalarMap(); - for (DSGraph::ScalarMapTy::iterator I = ValMap.begin(), - E = ValMap.end(); I != E; ++I) - GVM[I->first].mergeWith(I->second); - - if ((++Count & 1) == 0) // Prune nodes out every other time... - ResultGraph->removeTriviallyDeadNodes(); - } - - // FIXME: Must recalculate and use the Incomplete markers!! - - // Now that we have all of the graphs inlined, we can go about eliminating - // call nodes... - // - std::vector<DSCallSite> &Calls = - ResultGraph->getAuxFunctionCalls(); - assert(Calls.empty() && "Aux call list is already in use??"); - - // Start with a copy of the original call sites... - Calls = ResultGraph->getFunctionCalls(); - - for (unsigned i = 0; i != Calls.size(); ) { - DSCallSite &CurCall = Calls[i]; - - // Loop over the called functions, eliminating as many as possible... - std::vector<GlobalValue*> CallTargets; - if (CurCall.isDirectCall()) - CallTargets.push_back(CurCall.getCalleeFunc()); - else - CallTargets = CurCall.getCalleeNode()->getGlobals(); - - for (unsigned c = 0; c != CallTargets.size(); ) { - // If we can eliminate this function call, do so! - bool Eliminated = false; - if (Function *F = dyn_cast<Function>(CallTargets[c])) - if (!F->isExternal()) { - ResolveFunctionCall(F, CurCall, RetValMap[F]); - Eliminated = true; - } - if (Eliminated) { - CallTargets[c] = CallTargets.back(); - CallTargets.pop_back(); - } else - ++c; // Cannot eliminate this call, skip over it... - } - - if (CallTargets.empty()) { // Eliminated all calls? - CurCall = Calls.back(); // Remove entry - Calls.pop_back(); - } else - ++i; // Skip this call site... - } - - RetValMap.clear(); - - // Update the "incomplete" markers on the nodes, ignoring unknownness due to - // incoming arguments... - ResultGraph->maskIncompleteMarkers(); - ResultGraph->markIncompleteNodes(DSGraph::IgnoreFormalArgs); - - // Remove any nodes that are dead after all of the merging we have done... - // FIXME: We should be able to disable the globals graph for steens! - ResultGraph->removeDeadNodes(DSGraph::KeepUnreachableGlobals); - - DEBUG(print(std::cerr, &M)); - return false; -} - -// alias - This is the only method here that does anything interesting... -AliasAnalysis::AliasResult Steens::alias(const Value *V1, unsigned V1Size, - const Value *V2, unsigned V2Size) { - // FIXME: HANDLE Size argument! - assert(ResultGraph && "Result graph has not been computed yet!"); - - DSGraph::ScalarMapTy &GSM = ResultGraph->getScalarMap(); - - DSGraph::ScalarMapTy::iterator I = GSM.find(const_cast<Value*>(V1)); - if (I != GSM.end() && I->second.getNode()) { - DSNodeHandle &V1H = I->second; - DSGraph::ScalarMapTy::iterator J=GSM.find(const_cast<Value*>(V2)); - if (J != GSM.end() && J->second.getNode()) { - DSNodeHandle &V2H = J->second; - // If the two pointers point to different data structure graph nodes, they - // cannot alias! - if (V1H.getNode() != V2H.getNode()) // FIXME: Handle incompleteness! - return NoAlias; - - // FIXME: If the two pointers point to the same node, and the offsets are - // different, and the LinkIndex vector doesn't alias the section, then the - // two pointers do not alias. We need access size information for the two - // accesses though! - // - } - } - - // If we cannot determine alias properties based on our graph, fall back on - // some other AA implementation. - // - return getAnalysis<AliasAnalysis>().alias(V1, V1Size, V2, V2Size); -}
diff --git a/poolalloc/lib/DSA/TopDownClosure.cpp b/poolalloc/lib/DSA/TopDownClosure.cpp deleted file mode 100644 index 92a03ee..0000000 --- a/poolalloc/lib/DSA/TopDownClosure.cpp +++ /dev/null
@@ -1,271 +0,0 @@ -//===- TopDownClosure.cpp - Compute the top-down interprocedure closure ---===// -// -// This file implements the TDDataStructures class, which represents the -// Top-down Interprocedural closure of the data structure graph over the -// program. This is useful (but not strictly necessary?) for applications -// like pointer analysis. -// -//===----------------------------------------------------------------------===// - -#include "llvm/Analysis/DataStructure.h" -#include "llvm/Module.h" -#include "llvm/DerivedTypes.h" -#include "Support/Debug.h" -#include "Support/Statistic.h" -#include "DSCallSiteIterator.h" - -namespace { - RegisterAnalysis<TDDataStructures> // Register the pass - Y("tddatastructure", "Top-down Data Structure Analysis"); - - Statistic<> NumTDInlines("tddatastructures", "Number of graphs inlined"); -} - -/// FunctionHasCompleteArguments - This function returns true if it is safe not -/// to mark arguments to the function complete. -/// -/// FIXME: Need to check if all callers have been found, or rather if a -/// funcpointer escapes! -/// -static bool FunctionHasCompleteArguments(Function &F) { - return F.hasInternalLinkage(); -} - -// run - Calculate the top down data structure graphs for each function in the -// program. -// -bool TDDataStructures::run(Module &M) { - BUDataStructures &BU = getAnalysis<BUDataStructures>(); - GlobalsGraph = new DSGraph(BU.getGlobalsGraph()); - - // Figure out which functions must not mark their arguments complete because - // they are accessible outside this compilation unit. - for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) - if (!FunctionHasCompleteArguments(*I)) - ArgsRemainIncomplete.insert(I); - - // We want to traverse the call graph in reverse post-order. To do this, we - // calculate a post-order traversal, then reverse it. - hash_set<DSGraph*> VisitedGraph; - std::vector<DSGraph*> PostOrder; - const BUDataStructures::ActualCalleesTy &ActualCallees = - getAnalysis<BUDataStructures>().getActualCallees(); - - // Calculate top-down from main... - if (Function *F = M.getMainFunction()) - ComputePostOrder(*F, VisitedGraph, PostOrder, ActualCallees); - - // Next calculate the graphs for each unreachable function... - for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) - ComputePostOrder(*I, VisitedGraph, PostOrder, ActualCallees); - - VisitedGraph.clear(); // Release memory! - - // Visit each of the graphs in reverse post-order now! - while (!PostOrder.empty()) { - inlineGraphIntoCallees(*PostOrder.back()); - PostOrder.pop_back(); - } - - ArgsRemainIncomplete.clear(); - return false; -} - - -DSGraph &TDDataStructures::getOrCreateDSGraph(Function &F) { - DSGraph *&G = DSInfo[&F]; - if (G == 0) { // Not created yet? Clone BU graph... - G = new DSGraph(getAnalysis<BUDataStructures>().getDSGraph(F)); - G->getAuxFunctionCalls().clear(); - G->setPrintAuxCalls(); - G->setGlobalsGraph(GlobalsGraph); - } - return *G; -} - - -void TDDataStructures::ComputePostOrder(Function &F,hash_set<DSGraph*> &Visited, - std::vector<DSGraph*> &PostOrder, - const BUDataStructures::ActualCalleesTy &ActualCallees) { - if (F.isExternal()) return; - DSGraph &G = getOrCreateDSGraph(F); - if (Visited.count(&G)) return; - Visited.insert(&G); - - // Recursively traverse all of the callee graphs. - const std::vector<DSCallSite> &FunctionCalls = G.getFunctionCalls(); - - for (unsigned i = 0, e = FunctionCalls.size(); i != e; ++i) { - std::pair<BUDataStructures::ActualCalleesTy::const_iterator, - BUDataStructures::ActualCalleesTy::const_iterator> - IP = ActualCallees.equal_range(&FunctionCalls[i].getCallInst()); - - for (BUDataStructures::ActualCalleesTy::const_iterator I = IP.first; - I != IP.second; ++I) - ComputePostOrder(*I->second, Visited, PostOrder, ActualCallees); - } - - PostOrder.push_back(&G); -} - - - - - -// releaseMemory - If the pass pipeline is done with this pass, we can release -// our memory... here... -// -// FIXME: This should be releaseMemory and will work fine, except that LoadVN -// has no way to extend the lifetime of the pass, which screws up ds-aa. -// -void TDDataStructures::releaseMyMemory() { - for (hash_map<Function*, DSGraph*>::iterator I = DSInfo.begin(), - E = DSInfo.end(); I != E; ++I) { - I->second->getReturnNodes().erase(I->first); - if (I->second->getReturnNodes().empty()) - delete I->second; - } - - // Empty map so next time memory is released, data structures are not - // re-deleted. - DSInfo.clear(); - delete GlobalsGraph; - GlobalsGraph = 0; -} - -void TDDataStructures::inlineGraphIntoCallees(DSGraph &Graph) { - // Recompute the Incomplete markers and eliminate unreachable nodes. - Graph.removeTriviallyDeadNodes(); - Graph.maskIncompleteMarkers(); - - // If any of the functions has incomplete incoming arguments, don't mark any - // of them as complete. - bool HasIncompleteArgs = false; - const DSGraph::ReturnNodesTy &GraphReturnNodes = Graph.getReturnNodes(); - for (DSGraph::ReturnNodesTy::const_iterator I = GraphReturnNodes.begin(), - E = GraphReturnNodes.end(); I != E; ++I) - if (ArgsRemainIncomplete.count(I->first)) { - HasIncompleteArgs = true; - break; - } - - // Now fold in the necessary globals from the GlobalsGraph. A global G - // must be folded in if it exists in the current graph (i.e., is not dead) - // and it was not inlined from any of my callers. If it was inlined from - // a caller, it would have been fully consistent with the GlobalsGraph - // in the caller so folding in is not necessary. Otherwise, this node came - // solely from this function's BU graph and so has to be made consistent. - // - Graph.updateFromGlobalGraph(); - - // Recompute the Incomplete markers. Depends on whether args are complete - unsigned Flags - = HasIncompleteArgs ? DSGraph::MarkFormalArgs : DSGraph::IgnoreFormalArgs; - Graph.markIncompleteNodes(Flags | DSGraph::IgnoreGlobals); - - // Delete dead nodes. Treat globals that are unreachable as dead also. - Graph.removeDeadNodes(DSGraph::RemoveUnreachableGlobals); - - // We are done with computing the current TD Graph! Now move on to - // inlining the current graph into the graphs for its callees, if any. - // - const std::vector<DSCallSite> &FunctionCalls = Graph.getFunctionCalls(); - if (FunctionCalls.empty()) { - DEBUG(std::cerr << " [TD] No callees for: " << Graph.getFunctionNames() - << "\n"); - return; - } - - // Now that we have information about all of the callees, propagate the - // current graph into the callees. Clone only the reachable subgraph at - // each call-site, not the entire graph (even though the entire graph - // would be cloned only once, this should still be better on average). - // - DEBUG(std::cerr << " [TD] Inlining '" << Graph.getFunctionNames() <<"' into " - << FunctionCalls.size() << " call nodes.\n"); - - const BUDataStructures::ActualCalleesTy &ActualCallees = - getAnalysis<BUDataStructures>().getActualCallees(); - - // Loop over all the call sites and all the callees at each call site. - // Clone and merge the reachable subgraph from the call into callee's graph. - // - for (unsigned i = 0, e = FunctionCalls.size(); i != e; ++i) { - // For each function in the invoked function list at this call site... - std::pair<BUDataStructures::ActualCalleesTy::const_iterator, - BUDataStructures::ActualCalleesTy::const_iterator> - IP = ActualCallees.equal_range(&FunctionCalls[i].getCallInst()); - - // Multiple callees may have the same graph, so try to inline and merge - // only once for each <callSite,calleeGraph> pair, not once for each - // <callSite,calleeFunction> pair; the latter will be correct but slower. - hash_set<DSGraph*> GraphsSeen; - - // Loop over each actual callee at this call site - for (BUDataStructures::ActualCalleesTy::const_iterator I = IP.first; - I != IP.second; ++I) { - DSGraph& CalleeGraph = getDSGraph(*I->second); - assert(&CalleeGraph != &Graph && "TD need not inline graph into self!"); - - // if this callee graph is already done at this site, skip this callee - if (GraphsSeen.find(&CalleeGraph) != GraphsSeen.end()) - continue; - GraphsSeen.insert(&CalleeGraph); - - // Get the root nodes for cloning the reachable subgraph into each callee: - // -- all global nodes that appear in both the caller and the callee - // -- return value at this call site, if any - // -- actual arguments passed at this call site - // -- callee node at this call site, if this is an indirect call (this may - // not be needed for merging, but allows us to create CS and therefore - // simplify the merging below). - hash_set<const DSNode*> RootNodeSet; - for (DSGraph::ScalarMapTy::const_iterator - SI = CalleeGraph.getScalarMap().begin(), - SE = CalleeGraph.getScalarMap().end(); SI != SE; ++SI) - if (GlobalValue* GV = dyn_cast<GlobalValue>(SI->first)) { - DSGraph::ScalarMapTy::const_iterator GI=Graph.getScalarMap().find(GV); - if (GI != Graph.getScalarMap().end()) - RootNodeSet.insert(GI->second.getNode()); - } - - if (const DSNode* RetNode = FunctionCalls[i].getRetVal().getNode()) - RootNodeSet.insert(RetNode); - - for (unsigned j=0, N=FunctionCalls[i].getNumPtrArgs(); j < N; ++j) - if (const DSNode* ArgTarget = FunctionCalls[i].getPtrArg(j).getNode()) - RootNodeSet.insert(ArgTarget); - - if (FunctionCalls[i].isIndirectCall()) - RootNodeSet.insert(FunctionCalls[i].getCalleeNode()); - - DEBUG(std::cerr << " [TD] Resolving arguments for callee graph '" - << CalleeGraph.getFunctionNames() - << "': " << I->second->getFunctionType()->getNumParams() - << " args\n at call site (DSCallSite*) 0x" - << &FunctionCalls[i] << "\n"); - - DSGraph::NodeMapTy NodeMapInCallee; // map from nodes to clones in callee - DSGraph::NodeMapTy CompletedMap; // unused map for nodes not to do - CalleeGraph.cloneReachableSubgraph(Graph, RootNodeSet, - NodeMapInCallee, CompletedMap, - DSGraph::StripModRefBits | - DSGraph::KeepAllocaBit); - - // Transform our call site info into the cloned version for CalleeGraph - DSCallSite CS(FunctionCalls[i], NodeMapInCallee); - - // Get the formal argument and return nodes for the called function - // and merge them with the cloned subgraph. Global nodes were merged - // already by cloneReachableSubgraph() above. - CalleeGraph.getCallSiteForArguments(*I->second).mergeWith(CS); - - ++NumTDInlines; - } - } - - DEBUG(std::cerr << " [TD] Done inlining into callees for: " - << Graph.getFunctionNames() << " [" << Graph.getGraphSize() << "+" - << Graph.getFunctionCalls().size() << "]\n"); -} -
diff --git a/poolalloc/lib/PoolAllocate/PoolAllocate.cpp b/poolalloc/lib/PoolAllocate/PoolAllocate.cpp deleted file mode 100644 index 685071f..0000000 --- a/poolalloc/lib/PoolAllocate/PoolAllocate.cpp +++ /dev/null
@@ -1,1191 +0,0 @@ -//===-- PoolAllocate.cpp - Pool Allocation Pass ---------------------------===// -// -// This transform changes programs so that disjoint data structures are -// allocated out of different pools of memory, increasing locality. -// -//===----------------------------------------------------------------------===// - -#define DEBUG_TYPE "PoolAllocation" -#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/Debug.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, uint } - // void *Data (the data) - // unsigned NodeSize (size of an allocated node) - // unsigned FreeablePool (are slabs in the pool freeable upon calls to - // poolfree?) - const Type *PoolDescType = - StructType::get(make_vector<const Type*>(VoidPtrTy, Type::UIntTy, - 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<TDDataStructures>(); - AU.addRequired<TargetData>(); -} - -// Prints out the functions mapped to the leader of the equivalence class they -// belong to. -void PoolAllocate::printFuncECs() { - std::map<Function*, Function*> &leaderMap = FuncECs.getLeaderMap(); - std::cerr << "Indirect Function Map \n"; - for (std::map<Function*, Function*>::iterator LI = leaderMap.begin(), - LE = leaderMap.end(); LI != LE; ++LI) { - std::cerr << LI->first->getName() << ": leader is " - << LI->second->getName() << "\n"; - } -} - -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"; - } -} - -void PoolAllocate::buildIndirectFunctionSets(Module &M) { - // Iterate over the module looking for indirect calls to functions - - // Get top down DSGraph for the functions - TDDS = &getAnalysis<TDDataStructures>(); - - for (Module::iterator MI = M.begin(), ME = M.end(); MI != ME; ++MI) { - - DEBUG(std::cerr << "Processing indirect calls function:" << MI->getName() << "\n"); - - if (MI->isExternal()) - continue; - - DSGraph &TDG = TDDS->getDSGraph(*MI); - - std::vector<DSCallSite> callSites = TDG.getFunctionCalls(); - - // For each call site in the function - // All the functions that can be called at the call site are put in the - // same equivalence class. - for (std::vector<DSCallSite>::iterator CSI = callSites.begin(), - CSE = callSites.end(); CSI != CSE ; ++CSI) { - if (CSI->isIndirectCall()) { - DSNode *DSN = CSI->getCalleeNode(); - if (DSN->isIncomplete()) - std::cerr << "Incomplete node " << CSI->getCallInst(); - // assert(DSN->isGlobalNode()); - const std::vector<GlobalValue*> &Callees = DSN->getGlobals(); - if (Callees.size() > 0) { - Function *firstCalledF = dyn_cast<Function>(*Callees.begin()); - FuncECs.addElement(firstCalledF); - CallInstTargets.insert(std::pair<CallInst*,Function*> - (&CSI->getCallInst(), - firstCalledF)); - if (Callees.size() > 1) { - for (std::vector<GlobalValue*>::const_iterator CalleesI = - Callees.begin()+1, CalleesE = Callees.end(); - CalleesI != CalleesE; ++CalleesI) { - Function *calledF = dyn_cast<Function>(*CalleesI); - FuncECs.unionSetsWith(firstCalledF, calledF); - CallInstTargets.insert(std::pair<CallInst*,Function*> - (&CSI->getCallInst(), calledF)); - } - } - } else { - std::cerr << "No targets " << CSI->getCallInst(); - } - } - } - } - - // Print the equivalence classes - DEBUG(printFuncECs()); -} - -bool PoolAllocate::run(Module &M) { - if (M.begin() == M.end()) return false; - CurModule = &M; - - AddPoolPrototypes(); - BU = &getAnalysis<BUDataStructures>(); - - buildIndirectFunctionSets(M); - - std::map<Function*, Function*> FuncMap; - - // Loop over the functions in the original program finding the pool desc. - // arguments necessary for each function that is indirectly callable. - // For each equivalence class, make a list of pool arguments and update - // the PoolArgFirst and PoolArgLast values for each function. - Module::iterator LastOrigFunction = --M.end(); - for (Module::iterator I = M.begin(); ; ++I) { - if (!I->isExternal()) - FindFunctionPoolArgs(*I); - if (I == LastOrigFunction) break; - } - - // Now clone a function using the pool arg list obtained in the previous - // pass over the modules. - // Loop over only the function initially in the program, don't traverse newly - // added ones. If the function uses memory, make its clone. - 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); - } - - if (CollapseFlag) - std::cerr << "Pool Allocation successful! However all data structures may not be pool allocated\n"; - - 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); - - // The poolallocarray function - FunctionType *PoolAllocArrayTy = - FunctionType::get(VoidPtrTy, - make_vector<const Type*>(PoolDescPtr, Type::UIntTy, 0), - false); - PoolAllocArray = CurModule->getOrInsertFunction("poolallocarray", - PoolAllocArrayTy); - -} - -// Inline the DSGraphs of functions corresponding to the potential targets at -// indirect call sites into the DS Graph of the callee. -// This is required to know what pools to create/pass at the call site in the -// caller -// -void PoolAllocate::InlineIndirectCalls(Function &F, DSGraph &G, - hash_set<Function*> &visited) { - std::vector<DSCallSite> callSites = G.getFunctionCalls(); - - visited.insert(&F); - - // For each indirect call site in the function, inline all the potential - // targets - for (std::vector<DSCallSite>::iterator CSI = callSites.begin(), - CSE = callSites.end(); CSI != CSE; ++CSI) { - if (CSI->isIndirectCall()) { - CallInst &CI = CSI->getCallInst(); - std::pair<std::multimap<CallInst*, Function*>::iterator, - std::multimap<CallInst*, Function*>::iterator> Targets = - CallInstTargets.equal_range(&CI); - for (std::multimap<CallInst*, Function*>::iterator TFI = Targets.first, - TFE = Targets.second; TFI != TFE; ++TFI) { - DSGraph &TargetG = BU->getDSGraph(*TFI->second); - // Call the function recursively if the callee is not yet inlined - // and if it hasn't been visited in this sequence of calls - // The latter is dependent on the fact that the graphs of all functions - // in an SCC are actually the same - if (InlinedFuncs.find(TFI->second) == InlinedFuncs.end() && - visited.find(TFI->second) == visited.end()) { - InlineIndirectCalls(*TFI->second, TargetG, visited); - } - G.mergeInGraph(*CSI, *TFI->second, TargetG, DSGraph::KeepModRefBits | - DSGraph::KeepAllocaBit | DSGraph::DontCloneCallNodes | - DSGraph::DontCloneAuxCallNodes); - } - } - } - - // Mark this function as one whose graph is inlined with its indirect - // function targets' DS Graphs. This ensures that every function is inlined - // exactly once - InlinedFuncs.insert(&F); -} - -void PoolAllocate::FindFunctionPoolArgs(Function &F) { - - // The DSGraph is merged with the globals graph. - DSGraph &G = BU->getDSGraph(F); - G.mergeInGlobalsGraph(); - - // Inline the potential targets of indirect calls - hash_set<Function*> visitedFuncs; - InlineIndirectCalls(F, G, visitedFuncs); - - // At this point the DS Graphs have been modified in place including - // information about globals as well as indirect calls, making it useful - // for pool allocation - std::vector<DSNode*> &Nodes = G.getNodes(); - if (Nodes.empty()) return ; // No memory activity, nothing is required - - FuncInfo &FI = FunctionInfo[&F]; // Create a new entry for F - - FI.Clone = 0; - - // Initialize the PoolArgFirst and PoolArgLast for the function depending - // on whether there have been other functions in the equivalence class - // that have pool arguments so far in the analysis. - if (!FuncECs.findClass(&F)) { - FI.PoolArgFirst = FI.PoolArgLast = 0; - } else { - if (EqClass2LastPoolArg.find(FuncECs.findClass(&F)) != - EqClass2LastPoolArg.end()) - FI.PoolArgFirst = FI.PoolArgLast = - EqClass2LastPoolArg[FuncECs.findClass(&F)] + 1; - else - FI.PoolArgFirst = FI.PoolArgLast = 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]->isGlobalNode() && !Nodes[i]->isIncomplete()) - DEBUG(std::cerr << "Global node is not Incomplete\n"); - if ((Nodes[i]->isIncomplete() || Nodes[i]->isGlobalNode()) && - Nodes[i]->isHeapNode()) - Nodes[i]->markReachableNodes(MarkedNodes); - } - - // Marked the returned node as alive... - if (DSNode *RetNode = G.getReturnNodeFor(F).getNode()) - if (RetNode->isHeapNode()) - RetNode->markReachableNodes(MarkedNodes); - - if (MarkedNodes.empty()) // We don't need to clone the function if there - return; // are no incoming arguments to be added. - - // Erase any marked node that is not a heap node - - for (hash_set<DSNode*>::iterator I = MarkedNodes.begin(), - E = MarkedNodes.end(); I != E; ) { - // erase invalidates hash_set iterators if the iterator points to the - // element being erased - if (!(*I)->isHeapNode()) - MarkedNodes.erase(I++); - else - ++I; - } - - FI.PoolArgLast += MarkedNodes.size(); - - - if (FuncECs.findClass(&F)) { - // Update the equivalence class last pool argument information - // only if there actually were pool arguments to the function. - // Also, there is no entry for the Eq. class in EqClass2LastPoolArg - // if there are no functions in the equivalence class with pool arguments. - if (FI.PoolArgLast != FI.PoolArgFirst) - EqClass2LastPoolArg[FuncECs.findClass(&F)] = FI.PoolArgLast - 1; - } - -} - -// 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; - - FuncInfo &FI = FunctionInfo[&F]; - - hash_set<DSNode*> &MarkedNodes = FI.MarkedNodes; - - if (!FuncECs.findClass(&F)) { - // Not in any equivalence class - if (MarkedNodes.empty()) - return 0; - } else { - // No need to clone if there are no pool arguments in any function in the - // equivalence class - if (!EqClass2LastPoolArg.count(FuncECs.findClass(&F))) - return 0; - } - - // 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; - if (!FuncECs.findClass(&F)) { - 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) { - 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! - - } - else { - // This function is a member of an equivalence class and needs to be cloned - ArgTys.reserve(OldFuncTy->getParamTypes().size() + - EqClass2LastPoolArg[FuncECs.findClass(&F)] + 1); - FI.ArgNodes.reserve(EqClass2LastPoolArg[FuncECs.findClass(&F)] + 1); - - for (int i = 0; i <= EqClass2LastPoolArg[FuncECs.findClass(&F)]; ++i) { - ArgTys.push_back(PoolDescPtr); // Add the appropriate # of pool - // descs - } - - for (hash_set<DSNode*>::iterator I = MarkedNodes.begin(), - E = MarkedNodes.end(); I != E; ++I) { - FI.ArgNodes.push_back(*I); - } - - assert ((FI.ArgNodes.size() == (unsigned) (FI.PoolArgLast - - FI.PoolArgFirst)) && - "Number of ArgNodes equal to the number of pool arguments used by this function"); - - if (FI.ArgNodes.empty()) return 0; - } - - - 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, GlobalValue::InternalLinkage, - 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(); - - if (FuncECs.findClass(&F)) { - // If the function belongs to an equivalence class - for (int i = 0; i <= EqClass2LastPoolArg[FuncECs.findClass(&F)]; ++i, - ++NI) - NI->setName("PDa"); - - NI = New->abegin(); - if (FI.PoolArgFirst > 0) - for (int i = 0; i < FI.PoolArgFirst; ++NI, ++i) - ; - - for (unsigned i = 0, e = FI.ArgNodes.size(); i != e; ++i, ++NI) - PoolDescriptors.insert(std::make_pair(FI.ArgNodes[i], NI)); - - NI = New->abegin(); - if (EqClass2LastPoolArg.count(FuncECs.findClass(&F))) - for (int i = 0; i <= EqClass2LastPoolArg[FuncECs.findClass(&F)]; ++i, ++NI) - ; - } else { - // If the function does not belong to an equivalence class - if (FI.ArgNodes.size()) - 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)); - } - NI = New->abegin(); - if (FI.ArgNodes.size()) - for (unsigned i = 0; i < FI.ArgNodes.size(); ++NI, ++i) - ; - } - - // Map the existing arguments of the old function to the corresponding - // arguments of the new function. - std::map<const Value*, Value*> ValueMap; - if (NI != New->aend()) - 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]->isHeapNode() && // Pick nodes with heap elems - !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, F, 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; - - // Void types in DS graph are never used - if (Node->getType() != Type::VoidTy) - ElSize = ConstantUInt::get(Type::UIntTy, TD.getTypeSize(Node->getType())); - else { - DEBUG(std::cerr << "Potential node collapsing in " << F.getName() - << ". All Data Structures may not be pool allocated\n"); - ElSize = ConstantUInt::get(Type::UIntTy, 0); - } - - // 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; // The Bottom-up DS Graph - DSGraph &TDG; // The Top-down DS Graph - FuncInfo &FI; - - FuncTransform(PoolAllocate &P, DSGraph &g, DSGraph &tdg, FuncInfo &fi) - : PAInfo(P), G(g), TDG(tdg), FI(fi) { - } - - void visitMallocInst(MallocInst &MI); - void visitFreeInst(FreeInst &FI); - void visitCallInst(CallInst &CI); - - // The following instructions are never modified by pool allocation - void visitBranchInst(BranchInst &I) { } - void visitBinaryOperator(Instruction &I) { } - void visitShiftInst (ShiftInst &I) { } - void visitSwitchInst (SwitchInst &I) { } - void visitCastInst (CastInst &I) { } - void visitAllocaInst(AllocaInst &I) { } - void visitLoadInst(LoadInst &I) { } - void visitGetElementPtrInst (GetElementPtrInst &I) { } - - void visitReturnInst(ReturnInst &I); - void visitStoreInst (StoreInst &I); - void visitPHINode(PHINode &I); - - void visitInstruction(Instruction &I) { - std::cerr << "PoolAllocate does not recognize this instruction\n"; - abort(); - } - - private: - DSNodeHandle& getDSNodeHFor(Value *V) { - // if (isa<Constant>(V)) - // return DSNodeHandle(); - - 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]; - } - - DSNodeHandle& getTDDSNodeHFor(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 TDG.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); - - if (I != FI.PoolDescriptors.end()) { - // Check that the node pointed to by V in the TD DS graph is not - // collapsed - DSNode *TDNode = getTDDSNodeHFor(V).getNode(); - if (TDNode->getType() != Type::VoidTy) - return I->second; - else { - PAInfo.CollapseFlag = 1; - return 0; - } - } - else - return 0; - - } - - bool isFuncPtr(Value *V); - - Function* getFuncClass(Value *V); - - Value* retCloneIfFunc(Value *V); - }; -} - -void PoolAllocate::TransformFunctionBody(Function &F, Function &OldF, - DSGraph &G, FuncInfo &FI) { - FuncTransform(*this, G, TDDS->getDSGraph(OldF), FI).visit(F); -} - -// Returns true if V is a function pointer -bool FuncTransform::isFuncPtr(Value *V) { - if (const PointerType *PTy = dyn_cast<PointerType>(V->getType())) - return isa<FunctionType>(PTy->getElementType()); - return false; -} - -// Given a function pointer, return the function eq. class if one exists -Function* FuncTransform::getFuncClass(Value *V) { - // Look at DSGraph and see if the set of of functions it could point to - // are pool allocated. - - if (!isFuncPtr(V)) - return 0; - - // Two cases: - // if V is a constant - if (Function *theFunc = dyn_cast<Function>(V)) { - if (!PAInfo.FuncECs.findClass(theFunc)) - // If this function does not belong to any equivalence class - return 0; - if (PAInfo.EqClass2LastPoolArg.count(PAInfo.FuncECs.findClass(theFunc))) - return PAInfo.FuncECs.findClass(theFunc); - else - return 0; - } - - // if V is not a constant - DSNode *DSN = TDG.getNodeForValue(V).getNode(); - if (!DSN) { - return 0; - } - const std::vector<GlobalValue*> &Callees = DSN->getGlobals(); - if (Callees.size() > 0) { - Function *calledF = dyn_cast<Function>(*Callees.begin()); - assert(PAInfo.FuncECs.findClass(calledF) && "should exist in some eq. class"); - if (PAInfo.EqClass2LastPoolArg.count(PAInfo.FuncECs.findClass(calledF))) - return PAInfo.FuncECs.findClass(calledF); - } - - return 0; -} - -// Returns the clone if V is a static function (not a pointer) and belongs -// to an equivalence class i.e. is pool allocated -Value* FuncTransform::retCloneIfFunc(Value *V) { - if (Function *fixedFunc = dyn_cast<Function>(V)) - if (getFuncClass(V)) - return PAInfo.getFuncInfo(*fixedFunc)->Clone; - - return 0; -} - -void FuncTransform::visitReturnInst (ReturnInst &RI) { - if (RI.getNumOperands()) - if (Value *clonedFunc = retCloneIfFunc(RI.getOperand(0))) { - // Cast the clone of RI.getOperand(0) to the non-pool-allocated type - CastInst *CastI = new CastInst(clonedFunc, RI.getOperand(0)->getType(), - "tmp", &RI); - // Insert return instruction that returns the casted value - ReturnInst *RetI = new ReturnInst(CastI, &RI); - - // Remove original return instruction - RI.getParent()->getInstList().erase(&RI); - - if (!FI.NewToOldValueMap.empty()) { - std::map<Value*,const Value*>::iterator II = - FI.NewToOldValueMap.find(&RI); - assert(II != FI.NewToOldValueMap.end() && - "RI not found in clone?"); - FI.NewToOldValueMap.insert(std::make_pair(RetI, II->second)); - FI.NewToOldValueMap.erase(II); - } - } -} - -void FuncTransform::visitStoreInst (StoreInst &SI) { - // Check if a constant function is being stored - if (Value *clonedFunc = retCloneIfFunc(SI.getOperand(0))) { - CastInst *CastI = new CastInst(clonedFunc, SI.getOperand(0)->getType(), - "tmp", &SI); - StoreInst *StoreI = new StoreInst(CastI, SI.getOperand(1), &SI); - SI.getParent()->getInstList().erase(&SI); - - // Update the NewToOldValueMap if this is a clone - if (!FI.NewToOldValueMap.empty()) { - std::map<Value*,const Value*>::iterator II = - FI.NewToOldValueMap.find(&SI); - assert(II != FI.NewToOldValueMap.end() && - "SI not found in clone?"); - FI.NewToOldValueMap.insert(std::make_pair(StoreI, II->second)); - FI.NewToOldValueMap.erase(II); - } - } -} - -void FuncTransform::visitPHINode(PHINode &PI) { - // If any of the operands of the PHI node is a constant function pointer - // that is cloned, the cast instruction has to be inserted at the end of the - // previous basic block - - if (isFuncPtr(&PI)) { - PHINode *V = new PHINode(PI.getType(), PI.getName(), &PI); - for (unsigned i = 0 ; i < PI.getNumIncomingValues(); ++i) { - if (Value *clonedFunc = retCloneIfFunc(PI.getIncomingValue(i))) { - // Insert CastInst at the end of PI.getIncomingBlock(i) - BasicBlock::iterator BBI = --PI.getIncomingBlock(i)->end(); - // BBI now points to the terminator instruction of the basic block. - CastInst *CastI = new CastInst(clonedFunc, PI.getType(), "tmp", BBI); - V->addIncoming(CastI, PI.getIncomingBlock(i)); - } else { - V->addIncoming(PI.getIncomingValue(i), PI.getIncomingBlock(i)); - } - - } - PI.replaceAllUsesWith(V); - PI.getParent()->getInstList().erase(&PI); - - DSGraph::ScalarMapTy &SM = G.getScalarMap(); - DSGraph::ScalarMapTy::iterator PII = SM.find(&PI); - - // Update Scalar map of DSGraph if this is one of the original functions - // Otherwise update the NewToOldValueMap - if (PII != SM.end()) { - SM.insert(std::make_pair(V, PII->second)); - SM.erase(PII); // Destroy the PHINode - } else { - std::map<Value*,const Value*>::iterator II = - FI.NewToOldValueMap.find(&PI); - assert(II != FI.NewToOldValueMap.end() && - "PhiI not found in clone?"); - FI.NewToOldValueMap.insert(std::make_pair(V, II->second)); - FI.NewToOldValueMap.erase(II); - } - } -} - -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; - - // Insert a call to poolalloc - Value *V; - if (MI.isArrayAllocation()) - V = new CallInst(PAInfo.PoolAllocArray, - make_vector(PH, MI.getOperand(0), 0), - MI.getName(), &MI); - else - V = new CallInst(PAInfo.PoolAlloc, make_vector(PH, 0), - MI.getName(), &MI); - - MI.setName(""); // Nuke MIs name - - Value *Casted = V; - - // Cast to the appropriate type if necessary - 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); - - // 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); - } -} - -static void CalcNodeMapping(DSNodeHandle& Caller, DSNodeHandle& Callee, - std::map<DSNode*, DSNode*> &NodeMapping) { - DSNode *CalleeNode = Callee.getNode(); - DSNode *CallerNode = Caller.getNode(); - - unsigned CalleeOffset = Callee.getOffset(); - unsigned CallerOffset = Caller.getOffset(); - - if (CalleeNode == 0) return; - - // If callee has a node and caller doesn't, then a constant argument was - // passed by the caller - if (CallerNode == 0) { - NodeMapping.insert(NodeMapping.end(), std::make_pair(CalleeNode, - (DSNode *) 0)); - } - - // Map the callee node to the caller node. - // NB: The callee node could be of a different type. Eg. if it points to the - // field of a struct that the caller points to - std::map<DSNode*, DSNode*>::iterator I = NodeMapping.find(CalleeNode); - if (I != NodeMapping.end()) { // Node already in map... - assert(I->second == CallerNode && - "Node maps to different nodes on paths?"); - } else { - NodeMapping.insert(I, std::make_pair(CalleeNode, CallerNode)); - - if (CalleeNode->getType() != CallerNode->getType() && CallerOffset == 0) - DEBUG(std::cerr << "NB: Mapping of nodes between different types\n"); - - // Recursively map the callee links to the caller links starting from the - // offset in the node into which they are mapped. - // Being a BU Graph, the callee ought to have smaller number of links unless - // there is collapsing in the caller - unsigned numCallerLinks = CallerNode->getNumLinks() - CallerOffset; - unsigned numCalleeLinks = CalleeNode->getNumLinks() - CalleeOffset; - - if (numCallerLinks > 0) { - if (numCallerLinks < numCalleeLinks) { - DEBUG(std::cerr << "Potential node collapsing in caller\n"); - for (unsigned i = 0, e = numCalleeLinks; i != e; ++i) - CalcNodeMapping(CallerNode->getLink(((i%numCallerLinks) << DS::PointerShift) + CallerOffset), CalleeNode->getLink((i << DS::PointerShift) + CalleeOffset), NodeMapping); - } else { - for (unsigned i = 0, e = numCalleeLinks; i != e; ++i) - CalcNodeMapping(CallerNode->getLink((i << DS::PointerShift) + CallerOffset), CalleeNode->getLink((i << DS::PointerShift) + CalleeOffset), NodeMapping); - } - } else if (numCalleeLinks > 0) { - DEBUG(std::cerr << - "Caller has unexpanded node, due to indirect call perhaps!\n"); - } - } -} - -void FuncTransform::visitCallInst(CallInst &CI) { - Function *CF = CI.getCalledFunction(); - - // optimization for function pointers that are basically gotten from a cast - // with only one use and constant expressions with casts in them - if (!CF) { - if (CastInst* CastI = dyn_cast<CastInst>(CI.getCalledValue())) { - 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>(CI.getOperand(0))) { - if (CE->getOpcode() == Instruction::Cast) { - if (isa<ConstantPointerRef>(CE->getOperand(0))) - return; - else - assert(0 && "Function pointer cast not handled as called function\n"); - } - } - - } - - DSGraph &CallerG = G; - - std::vector<Value*> Args; - if (!CF) { // Indirect call - DEBUG(std::cerr << " Handling call: " << CI); - - std::map<unsigned, Value*> PoolArgs; - Function *FuncClass; - - std::pair<std::multimap<CallInst*, Function*>::iterator, - std::multimap<CallInst*, Function*>::iterator> Targets = - PAInfo.CallInstTargets.equal_range(&CI); - for (std::multimap<CallInst*, Function*>::iterator TFI = Targets.first, - TFE = Targets.second; TFI != TFE; ++TFI) { - if (TFI == Targets.first) { - FuncClass = PAInfo.FuncECs.findClass(TFI->second); - // Nothing to transform if there are no pool arguments in this - // equivalence class of functions. - if (!PAInfo.EqClass2LastPoolArg.count(FuncClass)) - return; - } - - FuncInfo *CFI = PAInfo.getFuncInfo(*TFI->second); - - if (!CFI->ArgNodes.size()) continue; // Nothing to transform... - - DSGraph &CG = PAInfo.getBUDataStructures().getDSGraph(*TFI->second); - std::map<DSNode*, DSNode*> NodeMapping; - - Function::aiterator AI = TFI->second->abegin(), AE = TFI->second->aend(); - unsigned OpNum = 1; - for ( ; AI != AE; ++AI, ++OpNum) { - if (!isa<Constant>(CI.getOperand(OpNum))) - CalcNodeMapping(getDSNodeHFor(CI.getOperand(OpNum)), - CG.getScalarMap()[AI], NodeMapping); - } - assert(OpNum == CI.getNumOperands() && "Varargs calls not handled yet!"); - - if (CI.getType() != Type::VoidTy) - CalcNodeMapping(getDSNodeHFor(&CI), - CG.getReturnNodeFor(*TFI->second), NodeMapping); - - // 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 (isa<GlobalValue>(SMI->first)) { - CalcNodeMapping(SMI->second, - CG.getScalarMap()[SMI->first], NodeMapping); - } - - unsigned idx = CFI->PoolArgFirst; - - // The following loop determines the pool pointers corresponding to - // CFI. - for (unsigned i = 0, e = CFI->ArgNodes.size(); i != e; ++i, ++idx) { - if (NodeMapping.count(CFI->ArgNodes[i])) { - assert(NodeMapping.count(CFI->ArgNodes[i]) && "Node not in mapping!"); - DSNode *LocalNode = NodeMapping.find(CFI->ArgNodes[i])->second; - if (LocalNode) { - assert(FI.PoolDescriptors.count(LocalNode) && - "Node not pool allocated?"); - PoolArgs[idx] = FI.PoolDescriptors.find(LocalNode)->second; - } - else - // LocalNode is null when a constant is passed in as a parameter - PoolArgs[idx] = Constant::getNullValue(PoolDescPtr); - } else { - PoolArgs[idx] = Constant::getNullValue(PoolDescPtr); - } - } - } - - // Push the pool arguments into Args. - if (PAInfo.EqClass2LastPoolArg.count(FuncClass)) { - for (int i = 0; i <= PAInfo.EqClass2LastPoolArg[FuncClass]; ++i) { - if (PoolArgs.find(i) != PoolArgs.end()) - Args.push_back(PoolArgs[i]); - else - Args.push_back(Constant::getNullValue(PoolDescPtr)); - } - - assert(Args.size()== (unsigned) PAInfo.EqClass2LastPoolArg[FuncClass] + 1 - && "Call has same number of pool args as the called function"); - } - - // Add the rest of the arguments (the original arguments of the function)... - Args.insert(Args.end(), CI.op_begin()+1, CI.op_end()); - - std::string Name = CI.getName(); - - Value *NewCall; - if (Args.size() > CI.getNumOperands() - 1) { - // If there are any pool arguments - CastInst *CastI = - new CastInst(CI.getOperand(0), - PAInfo.getFuncInfo(*FuncClass)->Clone->getType(), "tmp", - &CI); - NewCall = new CallInst(CastI, Args, Name, &CI); - } else { - NewCall = new CallInst(CI.getOperand(0), Args, Name, &CI); - } - - CI.replaceAllUsesWith(NewCall); - DEBUG(std::cerr << " Result Call: " << *NewCall); - - if (CI.getType() != Type::VoidTy) { - // If we are modifying the original function, update the DSGraph... - DSGraph::ScalarMapTy &SM = G.getScalarMap(); - DSGraph::ScalarMapTy::iterator CII = SM.find(&CI); - 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(&CI); - 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(&CI); - assert(II != FI.NewToOldValueMap.end() && - "CI not found in clone?"); - FI.NewToOldValueMap.insert(std::make_pair(NewCall, II->second)); - FI.NewToOldValueMap.erase(II); - } - } - else { - - 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) { - Value *callOp = CI.getOperand(OpNum); - if (!isa<Constant>(callOp)) - CalcNodeMapping(getDSNodeHFor(callOp), CG.getScalarMap()[AI], - NodeMapping); - } - assert(OpNum == CI.getNumOperands() && "Varargs calls not handled yet!"); - - // Map the return value as well... - if (CI.getType() != Type::VoidTy) - CalcNodeMapping(getDSNodeHFor(&CI), CG.getReturnNodeFor(*CF), - NodeMapping); - - // 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 (isa<GlobalValue>(SMI->first)) { - CalcNodeMapping(SMI->second, - CG.getScalarMap()[SMI->first], NodeMapping); - } - - // Okay, now that we have established our mapping, we can figure out which - // pool descriptors to pass in... - - // Add an argument for each pool which must be passed in... - if (CFI->PoolArgFirst != 0) { - for (int i = 0; i < CFI->PoolArgFirst; ++i) - Args.push_back(Constant::getNullValue(PoolDescPtr)); - } - - for (unsigned i = 0, e = CFI->ArgNodes.size(); i != e; ++i) { - if (NodeMapping.count(CFI->ArgNodes[i])) { - - DSNode *LocalNode = NodeMapping.find(CFI->ArgNodes[i])->second; - if (LocalNode) { - assert(FI.PoolDescriptors.count(LocalNode) && - "Node not pool allocated?"); - Args.push_back(FI.PoolDescriptors.find(LocalNode)->second); - } else - Args.push_back(Constant::getNullValue(PoolDescPtr)); - } else { - Args.push_back(Constant::getNullValue(PoolDescPtr)); - } - } - - Function *FuncClass = PAInfo.FuncECs.findClass(CF); - - if (PAInfo.EqClass2LastPoolArg.count(FuncClass)) - for (int i = CFI->PoolArgLast; - i <= PAInfo.EqClass2LastPoolArg[FuncClass]; ++i) - 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(); - - std::map<Value*,const Value*>::iterator CNewII; - - Value *NewCall = new CallInst(CFI->Clone, Args, Name, &CI); - - CI.replaceAllUsesWith(NewCall); - DEBUG(std::cerr << " Result Call: " << *NewCall); - - if (CI.getType() != Type::VoidTy) { - // If we are modifying the original function, update the DSGraph... - DSGraph::ScalarMapTy &SM = G.getScalarMap(); - DSGraph::ScalarMapTy::iterator CII = SM.find(&CI); - 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 CNII = - FI.NewToOldValueMap.find(&CI); - assert(CNII != FI.NewToOldValueMap.end() && CNII->second && - "CI not found in clone?"); - FI.NewToOldValueMap.insert(std::make_pair(NewCall, CNII->second)); - FI.NewToOldValueMap.erase(CNII); - } - } else if (!FI.NewToOldValueMap.empty()) { - std::map<Value*,const Value*>::iterator II = - FI.NewToOldValueMap.find(&CI); - assert(II != FI.NewToOldValueMap.end() && "CI not found in clone?"); - FI.NewToOldValueMap.insert(std::make_pair(NewCall, II->second)); - FI.NewToOldValueMap.erase(II); - } - } - - CI.getParent()->getInstList().erase(&CI); -}
diff --git a/poolalloc/lib/PoolAllocate/PoolAllocate.h b/poolalloc/lib/PoolAllocate/PoolAllocate.h deleted file mode 100644 index b6806c1..0000000 --- a/poolalloc/lib/PoolAllocate/PoolAllocate.h +++ /dev/null
@@ -1,156 +0,0 @@ -//===-- PoolAllocate.h - Pool allocation pass -------------------*- C++ -*-===// -// -// This transform changes programs so that disjoint data structures are -// allocated out of different pools of memory, increasing locality. This header -// file exposes information about the pool allocation itself so that follow-on -// passes may extend or use the pool allocation for analysis. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_TRANSFORMS_POOLALLOCATE_H -#define LLVM_TRANSFORMS_POOLALLOCATE_H - -#include "llvm/Pass.h" -#include "Support/hash_set" -#include "Support/EquivalenceClasses.h" -class BUDataStructures; -class TDDataStructures; -class DSNode; -class DSGraph; -class CallInst; - -namespace PA { - /// FuncInfo - Represent the pool allocation information for one function in - /// the program. Note that many functions must actually be cloned in order - /// for pool allocation to add arguments to the function signature. In this - /// case, the Clone and NewToOldValueMap information identify how the clone - /// maps to the original function... - /// - struct FuncInfo { - /// MarkedNodes - The set of nodes which are not locally pool allocatable in - /// the current function. - /// - hash_set<DSNode*> MarkedNodes; - - /// Clone - The cloned version of the function, if applicable. - Function *Clone; - - /// ArgNodes - The list of DSNodes which have pools passed in as arguments. - /// - std::vector<DSNode*> ArgNodes; - - /// In order to handle indirect functions, the start and end of the - /// arguments that are useful to this function. - /// The pool arguments useful to this function are PoolArgFirst to - /// PoolArgLast not inclusive. - int PoolArgFirst, PoolArgLast; - - /// PoolDescriptors - The Value* (either an argument or an alloca) which - /// defines the pool descriptor for this DSNode. Pools are mapped one to - /// one with nodes in the DSGraph, so this contains a pointer to the node it - /// corresponds to. In addition, the pool is initialized by calling the - /// "poolinit" library function with a chunk of memory allocated with an - /// alloca instruction. This entry contains a pointer to that alloca if the - /// pool is locally allocated or the argument it is passed in through if - /// not. - /// Note: Does not include pool arguments that are passed in because of - /// indirect function calls that are not used in the function. - std::map<DSNode*, Value*> PoolDescriptors; - - /// NewToOldValueMap - When and if a function needs to be cloned, this map - /// contains a mapping from all of the values in the new function back to - /// the values they correspond to in the old function. - /// - std::map<Value*, const Value*> NewToOldValueMap; - }; -} - -/// PoolAllocate - The main pool allocation pass -/// -class PoolAllocate : public Pass { - Module *CurModule; - BUDataStructures *BU; - - TDDataStructures *TDDS; - - hash_set<Function*> InlinedFuncs; - - std::map<Function*, PA::FuncInfo> FunctionInfo; - - void buildIndirectFunctionSets(Module &M); - - void FindFunctionPoolArgs(Function &F); - - // Debug function to print the FuncECs - void printFuncECs(); - - public: - Function *PoolInit, *PoolDestroy, *PoolAlloc, *PoolAllocArray, *PoolFree; - - // Equivalence class where functions that can potentially be called via - // the same function pointer are in the same class. - EquivalenceClasses<Function *> FuncECs; - - // Map from an Indirect CallInst to the set of Functions that it can point to - std::multimap<CallInst *, Function *> CallInstTargets; - - // This maps an equivalence class to the last pool argument number for that - // class. This is used because the pool arguments for all functions within - // an equivalence class is passed to all the functions in that class. - // If an equivalence class does not require pool arguments, it is not - // on this map. - std::map<Function *, int> EqClass2LastPoolArg; - - // Exception flags - // CollapseFlag set if all data structures are not pool allocated, due to - // collapsing of nodes in the DS graph - unsigned CollapseFlag; - - public: - bool run(Module &M); - - virtual void getAnalysisUsage(AnalysisUsage &AU) const; - - BUDataStructures &getBUDataStructures() const { return *BU; } - - PA::FuncInfo *getFuncInfo(Function &F) { - std::map<Function*, PA::FuncInfo>::iterator I = FunctionInfo.find(&F); - return I != FunctionInfo.end() ? &I->second : 0; - } - - Module *getCurModule() { return CurModule; } - - private: - - /// AddPoolPrototypes - Add prototypes for the pool functions to the - /// specified module and update the Pool* instance variables to point to - /// them. - /// - void AddPoolPrototypes(); - - /// 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 *MakeFunctionClone(Function &F); - - /// ProcessFunctionBody - Rewrite the body of a transformed function to use - /// pool allocation where appropriate. - /// - void ProcessFunctionBody(Function &Old, Function &New); - - /// 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 CreatePools(Function &F, const std::vector<DSNode*> &NodesToPA, - std::map<DSNode*, Value*> &PoolDescriptors); - - void TransformFunctionBody(Function &F, Function &OldF, - DSGraph &G, PA::FuncInfo &FI); - - void InlineIndirectCalls(Function &F, DSGraph &G, - hash_set<Function*> &visited); -}; - -#endif
diff --git a/poolalloc/runtime/PoolAllocator/PoolAllocatorBitMask.cpp b/poolalloc/runtime/PoolAllocator/PoolAllocatorBitMask.cpp deleted file mode 100644 index b3a715f..0000000 --- a/poolalloc/runtime/PoolAllocator/PoolAllocatorBitMask.cpp +++ /dev/null
@@ -1,460 +0,0 @@ -#include <assert.h> -#include <stdio.h> -#include <stdlib.h> - -#undef assert -#define assert(X) - - -/* In the current implementation, each slab in the pool has NODES_PER_SLAB - * nodes unless the isSingleArray flag is set in which case it contains a - * single array of size ArraySize. Small arrays (size <= NODES_PER_SLAB) are - * still allocated in the slabs of size NODES_PER_SLAB - */ -#define NODES_PER_SLAB 512 - -typedef struct PoolTy { - void *Data; - unsigned NodeSize; - unsigned FreeablePool; /* Set to false if the memory from this pool cannot be - freed before destroy*/ - -} PoolTy; - -/* PoolSlab Structure - Hold NODES_PER_SLAB objects of the current node type. - * Invariants: FirstUnused <= LastUsed+1 - */ -typedef struct PoolSlab { - unsigned FirstUnused; /* First empty node in slab */ - int LastUsed; /* Last allocated node in slab */ - struct PoolSlab *Next; - unsigned char AllocatedBitVector[NODES_PER_SLAB/8]; - unsigned char StartOfAllocation[NODES_PER_SLAB/8]; - - unsigned isSingleArray; /* If this slab is used for exactly one array */ - /* The array is allocated from the start to the end of the slab */ - unsigned ArraySize; /* The size of the array allocated */ - - char Data[1]; /* Buffer to hold data in this slab... variable sized */ - -} PoolSlab; - -#define NODE_ALLOCATED(POOLSLAB, NODENUM) \ - ((POOLSLAB)->AllocatedBitVector[(NODENUM) >> 3] & (1 << ((NODENUM) & 7))) -#define MARK_NODE_ALLOCATED(POOLSLAB, NODENUM) \ - (POOLSLAB)->AllocatedBitVector[(NODENUM) >> 3] |= 1 << ((NODENUM) & 7) -#define MARK_NODE_FREE(POOLSLAB, NODENUM) \ - (POOLSLAB)->AllocatedBitVector[(NODENUM) >> 3] &= ~(1 << ((NODENUM) & 7)) -#define ALLOCATION_BEGINS(POOLSLAB, NODENUM) \ - ((POOLSLAB)->StartOfAllocation[(NODENUM) >> 3] & (1 << ((NODENUM) & 7))) -#define SET_START_BIT(POOLSLAB, NODENUM) \ - (POOLSLAB)->StartOfAllocation[(NODENUM) >> 3] |= 1 << ((NODENUM) & 7) -#define CLEAR_START_BIT(POOLSLAB, NODENUM) \ - (POOLSLAB)->StartOfAllocation[(NODENUM) >> 3] &= ~(1 << ((NODENUM) & 7)) - - -/* poolinit - Initialize a pool descriptor to empty - */ -void poolinit(PoolTy *Pool, unsigned NodeSize) { - if (!Pool) { - printf("Null pool pointer passed into poolinit!\n"); - exit(1); - } - - Pool->NodeSize = NodeSize; - Pool->Data = 0; - - Pool->FreeablePool = 1; - -} - -void poolmakeunfreeable(PoolTy *Pool) { - if (!Pool) { - printf("Null pool pointer passed in to poolmakeunfreeable!\n"); - exit(1); - } - - Pool->FreeablePool = 0; -} - -/* pooldestroy - Release all memory allocated for a pool - */ -void pooldestroy(PoolTy *Pool) { - PoolSlab *PS; - if (!Pool) { - printf("Null pool pointer passed in to pooldestroy!\n"); - exit(1); - } - - PS = (PoolSlab*)Pool->Data; - while (PS) { - PoolSlab *Next = PS->Next; - free(PS); - PS = Next; - } -} - -static void *FindSlabEntry(PoolSlab *PS, unsigned NodeSize) { - /* Loop through all of the slabs looking for one with an opening */ - for (; PS; PS = PS->Next) { - - /* If the slab is a single array, go on to the next slab */ - /* Don't allocate single nodes in a SingleArray slab */ - if (PS->isSingleArray) - continue; - - /* Check to see if there are empty entries at the end of the slab... */ - if (PS->LastUsed < NODES_PER_SLAB-1) { - /* Mark the returned entry used */ - MARK_NODE_ALLOCATED(PS, PS->LastUsed+1); - SET_START_BIT(PS, PS->LastUsed+1); - - /* If we are allocating out the first unused field, bump its index also */ - if (PS->FirstUnused == PS->LastUsed+1) - PS->FirstUnused++; - - /* Return the entry, increment LastUsed field. */ - return &PS->Data[0] + ++PS->LastUsed * NodeSize; - } - - /* If not, check to see if this node has a declared "FirstUnused" value that - * is less than the number of nodes allocated... - */ - if (PS->FirstUnused < NODES_PER_SLAB) { - /* Successfully allocate out the first unused node */ - unsigned Idx = PS->FirstUnused; - - MARK_NODE_ALLOCATED(PS, Idx); - SET_START_BIT(PS, Idx); - - /* Increment FirstUnused to point to the new first unused value... */ - do { - ++PS->FirstUnused; - } while (PS->FirstUnused < NODES_PER_SLAB && - NODE_ALLOCATED(PS, PS->FirstUnused)); - - return &PS->Data[0] + Idx*NodeSize; - } - } - - /* No empty nodes available, must grow # slabs! */ - return 0; -} - -char *poolalloc(PoolTy *Pool) { - unsigned NodeSize; - PoolSlab *PS; - void *Result; - - if (!Pool) { - printf("Null pool pointer passed in to poolalloc!\n"); - exit(1); - } - - NodeSize = Pool->NodeSize; - // Return if this pool has size 0 - if (NodeSize == 0) - return 0; - - PS = (PoolSlab*)Pool->Data; - - if ((Result = FindSlabEntry(PS, NodeSize))) - return Result; - - /* Otherwise we must allocate a new slab and add it to the list */ - PS = (PoolSlab*)malloc(sizeof(PoolSlab)+NodeSize*NODES_PER_SLAB-1); - - if (!PS) { - printf("poolalloc: Could not allocate memory!"); - exit(1); - } - - /* Initialize the slab to indicate that the first element is allocated */ - PS->FirstUnused = 1; - PS->LastUsed = 0; - /* This is not a single array */ - PS->isSingleArray = 0; - PS->ArraySize = 0; - - MARK_NODE_ALLOCATED(PS, 0); - SET_START_BIT(PS, 0); - - /* Add the slab to the list... */ - PS->Next = (PoolSlab*)Pool->Data; - Pool->Data = PS; - return &PS->Data[0]; -} - -void poolfree(PoolTy *Pool, char *Node) { - unsigned NodeSize, Idx; - PoolSlab *PS; - PoolSlab **PPS; - unsigned idxiter; - - if (!Pool) { - printf("Null pool pointer passed in to poolfree!\n"); - exit(1); - } - - NodeSize = Pool->NodeSize; - - // Return if this pool has size 0 - if (NodeSize == 0) - return; - - PS = (PoolSlab*)Pool->Data; - PPS = (PoolSlab**)&Pool->Data; - - /* Search for the slab that contains this node... */ - while (&PS->Data[0] > Node || &PS->Data[NodeSize*NODES_PER_SLAB-1] < Node) { - if (!PS) { - printf("poolfree: node being free'd not found in allocation pool specified!\n"); - exit(1); - } - - PPS = &PS->Next; - PS = PS->Next; - } - - /* PS now points to the slab where Node is */ - - Idx = (Node-&PS->Data[0])/NodeSize; - assert(Idx < NODES_PER_SLAB && "Pool slab searching loop broken!"); - - if (PS->isSingleArray) { - - /* If this slab is a SingleArray */ - - if (Idx != 0) { - printf("poolfree: Attempt to free middle of allocated array\n"); - exit(1); - } - if (!NODE_ALLOCATED(PS,0)) { - printf("poolfree: Attempt to free node that is already freed\n"); - exit(1); - } - /* Mark this SingleArray slab as being free by just marking the first - entry as free*/ - MARK_NODE_FREE(PS, 0); - } else { - - /* If this slab is not a SingleArray */ - - if (!ALLOCATION_BEGINS(PS, Idx)) { - printf("poolfree: Attempt to free middle of allocated array\n"); - } - - /* Free the first node */ - if (!NODE_ALLOCATED(PS, Idx)) { - printf("poolfree: Attempt to free node that is already freed\n"); - exit(1); - } - CLEAR_START_BIT(PS, Idx); - MARK_NODE_FREE(PS, Idx); - - // Free all nodes - idxiter = Idx + 1; - while (idxiter < NODES_PER_SLAB && (!ALLOCATION_BEGINS(PS,idxiter)) && - (NODE_ALLOCATED(PS, idxiter))) { - MARK_NODE_FREE(PS, idxiter); - ++idxiter; - } - - /* Update the first free field if this node is below the free node line */ - if (Idx < PS->FirstUnused) PS->FirstUnused = Idx; - - /* If we are not freeing the last element in a slab... */ - if (idxiter - 1 != PS->LastUsed) { - return; - } - - /* Otherwise we are freeing the last element in a slab... shrink the - * LastUsed marker down to last used node. - */ - PS->LastUsed = Idx; - do { - --PS->LastUsed; - /* Fixme, this should scan the allocated array an entire byte at a time - * for performance! - */ - } while (PS->LastUsed >= 0 && (!NODE_ALLOCATED(PS, PS->LastUsed))); - - assert(PS->FirstUnused <= PS->LastUsed+1 && - "FirstUnused field was out of date!"); - } - - /* Ok, if this slab is empty, we unlink it from the of slabs and either move - * it to the head of the list, or free it, depending on whether or not there - * is already an empty slab at the head of the list. - */ - /* Do this only if the pool is freeable */ - if (Pool->FreeablePool) { - if (PS->isSingleArray) { - /* If it is a SingleArray, just free it */ - *PPS = PS->Next; - free(PS); - } else if (PS->LastUsed == -1) { /* Empty slab? */ - PoolSlab *HeadSlab; - *PPS = PS->Next; /* Unlink from the list of slabs... */ - - HeadSlab = (PoolSlab*)Pool->Data; - if (HeadSlab && HeadSlab->LastUsed == -1){/*List already has empty slab?*/ - free(PS); /*Free memory for slab */ - } else { - PS->Next = HeadSlab; /*No empty slab yet, add this*/ - Pool->Data = PS; /*one to the head of the list */ - } - } - } else { - /* Pool is not freeable for safety reasons */ - /* Leave it in the list of PoolSlabs as an empty PoolSlab */ - if (!PS->isSingleArray) - if (PS->LastUsed == -1) { - PS->FirstUnused = 0; - - /* Do not free the pool, but move it to the head of the list if there is - no empty slab there already */ - PoolSlab *HeadSlab; - HeadSlab = (PoolSlab*)Pool->Data; - if (HeadSlab && HeadSlab->LastUsed != -1) { - PS->Next = HeadSlab; - Pool->Data = PS; - } - } - } -} - -/* The poolallocarray version of FindSlabEntry */ -static void *FindSlabEntryArray(PoolSlab *PS, unsigned NodeSize, - unsigned Size) { - unsigned i; - - /* Loop through all of the slabs looking for one with an opening */ - for (; PS; PS = PS->Next) { - - /* For large array allocation */ - if (Size > NODES_PER_SLAB) { - /* If this slab is a SingleArray that is free with size > Size, use it */ - if (PS->isSingleArray && !NODE_ALLOCATED(PS,0) && PS->ArraySize >= Size) { - /* Allocate the array in this slab */ - MARK_NODE_ALLOCATED(PS,0); /* In a single array, only the first node - needs to be marked */ - return &PS->Data[0]; - } else - continue; - } else if (PS->isSingleArray) - continue; /* Do not allocate small arrays in SingleArray slabs */ - - /* For small array allocation */ - /* Check to see if there are empty entries at the end of the slab... */ - if (PS->LastUsed < NODES_PER_SLAB-Size) { - /* Mark the returned entry used and set the start bit*/ - SET_START_BIT(PS, PS->LastUsed + 1); - for (i = PS->LastUsed + 1; i <= PS->LastUsed + Size; ++i) - MARK_NODE_ALLOCATED(PS, i); - - /* If we are allocating out the first unused field, bump its index also */ - if (PS->FirstUnused == PS->LastUsed+1) - PS->FirstUnused += Size; - - /* Increment LastUsed */ - PS->LastUsed += Size; - - /* Return the entry */ - return &PS->Data[0] + (PS->LastUsed - Size + 1) * NodeSize; - } - - /* If not, check to see if this node has a declared "FirstUnused" value - * starting which Size nodes can be allocated - */ - if (PS->FirstUnused < NODES_PER_SLAB - Size + 1 && - (PS->LastUsed < PS->FirstUnused || - PS->LastUsed - PS->FirstUnused >= Size)) { - unsigned Idx = PS->FirstUnused, foundArray; - - /* Check if there is a continuous array of Size nodes starting - FirstUnused */ - foundArray = 1; - for (i = Idx; (i < Idx + Size) && foundArray; ++i) - if (NODE_ALLOCATED(PS, i)) - foundArray = 0; - - if (foundArray) { - /* Successfully allocate starting from the first unused node */ - SET_START_BIT(PS, Idx); - for (i = Idx; i < Idx + Size; ++i) - MARK_NODE_ALLOCATED(PS, i); - - PS->FirstUnused += Size; - while (PS->FirstUnused < NODES_PER_SLAB && - NODE_ALLOCATED(PS, PS->FirstUnused)) { - ++PS->FirstUnused; - } - return &PS->Data[0] + Idx*NodeSize; - } - - } - } - - /* No empty nodes available, must grow # slabs! */ - return 0; -} - -char* poolallocarray(PoolTy* Pool, unsigned Size) { - unsigned NodeSize; - PoolSlab *PS; - void *Result; - unsigned i; - - if (!Pool) { - printf("Null pool pointer passed to poolallocarray!\n"); - exit(1); - } - - NodeSize = Pool->NodeSize; - - // Return if this pool has size 0 - if (NodeSize == 0) - return 0; - - PS = (PoolSlab*)Pool->Data; - - if ((Result = FindSlabEntryArray(PS, NodeSize,Size))) - return Result; - - /* Otherwise we must allocate a new slab and add it to the list */ - if (Size > NODES_PER_SLAB) { - /* Allocate a new slab of size Size */ - PS = (PoolSlab*)malloc(sizeof(PoolSlab)+NodeSize*Size-1); - if (!PS) { - printf("poolallocarray: Could not allocate memory!\n"); - exit(1); - } - PS->isSingleArray = 1; - PS->ArraySize = Size; - MARK_NODE_ALLOCATED(PS, 0); - } else { - PS = (PoolSlab*)malloc(sizeof(PoolSlab)+NodeSize*NODES_PER_SLAB-1); - if (!PS) { - printf("poolallocarray: Could not allocate memory!\n"); - exit(1); - } - - /* Initialize the slab to indicate that the first element is allocated */ - PS->FirstUnused = Size; - PS->LastUsed = Size - 1; - - PS->isSingleArray = 0; - PS->ArraySize = 0; - - SET_START_BIT(PS, 0); - for (i = 0; i < Size; ++i) { - MARK_NODE_ALLOCATED(PS, i); - } - } - - /* Add the slab to the list... */ - PS->Next = (PoolSlab*)Pool->Data; - Pool->Data = PS; - return &PS->Data[0]; -}