From d9ddf050141bb158ab77ccc3c482b44ab2f66268 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Tue, 26 Mar 2002 22:38:45 +0000 Subject: [PATCH] New header file for datastructure analysis git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@1993 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/Analysis/DataStructure.h | 377 ++++++++++++++++++ .../Analysis/DataStructure/DataStructure.h | 377 ++++++++++++++++++ 2 files changed, 754 insertions(+) create mode 100644 include/llvm/Analysis/DataStructure.h create mode 100644 include/llvm/Analysis/DataStructure/DataStructure.h diff --git a/include/llvm/Analysis/DataStructure.h b/include/llvm/Analysis/DataStructure.h new file mode 100644 index 00000000000..d84530313e8 --- /dev/null +++ b/include/llvm/Analysis/DataStructure.h @@ -0,0 +1,377 @@ +//===- DataStructure.h - Build a Module's call graph -------------*- 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 + +class Type; +class CallInst; +class AllocationInst; +class FunctionArgument; +class DSNode; +class FunctionRepBuilder; +class GlobalValue; +class FunctionDSGraph; +class DataStructure; + +// FIXME: move this somewhere private +unsigned countPointerFields(const Type *Ty); + +// PointerVal - Represent a pointer to a datastructure. The pointer points to +// a node, and can index into it. This is used for getelementptr instructions, +// which do not affect which node a pointer points to, but does change the field +// index +// +struct PointerVal { + DSNode *Node; + unsigned Index; // Index into Node->FieldLinks[] +public: + PointerVal(DSNode *N, unsigned Idx = 0) : Node(N), Index(Idx) {} + + DSNode *getNode() const { return Node; } + unsigned getIndex() const { return Index; } + + inline bool operator==(DSNode *N) const { return Node == N; } + inline bool operator!=(DSNode *N) const { return Node != N; } + + inline bool operator==(const PointerVal &PV) const { + return Node == PV.Node && Index == PV.Index; + } + inline bool operator!=(const PointerVal &PV) const { return !operator==(PV); } + + void print(std::ostream &O) const; +}; + + +// PointerValSet - This class represents a list of pointer values. The add +// method is used to add values to the set, and ensures that duplicates cannot +// happen. +// +class PointerValSet { + std::vector Vals; + void dropRefs(); + void addRefs(); +public: + PointerValSet() {} + PointerValSet(const PointerValSet &PVS) : Vals(PVS.Vals) { addRefs(); } + ~PointerValSet() { dropRefs(); } + const PointerValSet &operator=(const PointerValSet &PVS); + + const PointerVal &operator[](unsigned i) const { return Vals[i]; } + + unsigned size() const { return Vals.size(); } + bool empty() const { return Vals.empty(); } + void clear() { dropRefs(); Vals.clear(); } + + // add - Add the specified pointer, or contents of the specified PVS to this + // pointer set. If a 'Pointer' value is provided, notify the underlying data + // structure node that the pointer is pointing to it, so that it can be + // invalidated if neccesary later. True is returned if the value is new to + // this pointer. + // + bool add(const PointerVal &PV, Value *Pointer = 0); + bool add(const PointerValSet &PVS, Value *Pointer = 0) { + bool Changed = false; + for (unsigned i = 0, e = PVS.size(); i != e; ++i) + Changed |= add(PVS[i], Pointer); + return Changed; + } + + // removePointerTo - Remove a single pointer val that points to the specified + // node... + void removePointerTo(DSNode *Node); + + void print(std::ostream &O) const; +}; + + +//===----------------------------------------------------------------------===// +// DSNode - Base class for all data structure nodes... +// +// This class keeps track of its type, the pointer fields in the data structure, +// and a list of LLVM values that are pointing to this node. +// +class DSNode { + friend class FunctionDSGraph; + const Type *Ty; + std::vector FieldLinks; + std::vector Pointers; // Values pointing to me... + std::vector Referrers; + + DSNode(const DSNode &); // DO NOT IMPLEMENT + void operator=(const DSNode &); // DO NOT IMPLEMENT +public: + enum NodeTy { + NewNode, CallNode, ShadowNode, ArgNode, GlobalNode + } NodeType; + + DSNode(enum NodeTy NT, const Type *T); + virtual ~DSNode() { + dropAllReferences(); + assert(Referrers.empty() && "Referrers to dead node exist!"); + } + + unsigned getNumLinks() const { return FieldLinks.size(); } + PointerValSet &getLink(unsigned i) { + assert(i < getNumLinks() && "Field links access out of range..."); + return FieldLinks[i]; + } + + // addReferrer - Keep the referrer set up to date... + void addReferrer(PointerValSet *PVS) { Referrers.push_back(PVS); } + void removeReferrer(PointerValSet *PVS); + const std::vector &getReferrers() const { return Referrers; } + + void addPointer(Value *V) { Pointers.push_back(V); } + const std::vector &getPointers() const { return Pointers; } + + const Type *getType() const { return Ty; } + + void print(std::ostream &O) const; + + virtual std::string getCaption() const = 0; + virtual const std::vector *getAuxLinks() const { + return 0; // Default to nothing... + } + + DSNode *clone() const { + DSNode *New = cloneImpl(); + // Add all of the pointers to the new node... + for (unsigned pn = 0, pe = Pointers.size(); pn != pe; ++pn) + New->addPointer(Pointers[pn]); + return New; + } + + + virtual void dropAllReferences() { + FieldLinks.clear(); + } + +protected: + virtual DSNode *cloneImpl() const = 0; + virtual void mapNode(std::map &NodeMap, + const DSNode *Old); +}; + + +// NewDSNode - Represent all allocation (malloc or alloca) in the program. +// +class NewDSNode : public DSNode { + AllocationInst *Allocation; +public: + NewDSNode(AllocationInst *V); + + virtual std::string getCaption() const; + + // Support type inquiry through isa, cast, and dyn_cast... + static bool classof(const NewDSNode *) { return true; } + static bool classof(const DSNode *N) { return N->NodeType == NewNode; } +protected: + virtual NewDSNode *cloneImpl() const { return new NewDSNode(Allocation); } +}; + + +// GlobalDSNode - Represent the memory location that a global variable occupies +// +class GlobalDSNode : public DSNode { + GlobalValue *Val; +public: + GlobalDSNode(GlobalValue *V); + + virtual std::string getCaption() const; + + // Support type inquiry through isa, cast, and dyn_cast... + static bool classof(const GlobalDSNode *) { return true; } + static bool classof(const DSNode *N) { return N->NodeType == GlobalNode; } +private: + virtual GlobalDSNode *cloneImpl() const { return new GlobalDSNode(Val); } +}; + + +// CallDSNode - Represent a call instruction in the program... +// +class CallDSNode : public DSNode { + CallInst *CI; + std::vector ArgLinks; +public: + CallDSNode(CallInst *CI); + + CallInst *getCall() const { return CI; } + + const std::vector *getAuxLinks() const { return &ArgLinks; } + virtual std::string getCaption() const; + + bool addArgValue(unsigned ArgNo, const PointerValSet &PVS) { + return ArgLinks[ArgNo].add(PVS); + } + + unsigned getNumArgs() const { return ArgLinks.size(); } + const PointerValSet &getArgValues(unsigned ArgNo) const { + assert(ArgNo < ArgLinks.size() && "Arg # out of range!"); + return ArgLinks[ArgNo]; + } + + virtual void dropAllReferences() { + DSNode::dropAllReferences(); + ArgLinks.clear(); + } + + + // Support type inquiry through isa, cast, and dyn_cast... + static bool classof(const CallDSNode *) { return true; } + static bool classof(const DSNode *N) { return N->NodeType == CallNode; } +private: + virtual CallDSNode *cloneImpl() const { return new CallDSNode(CI); } + virtual void mapNode(std::map &NodeMap, + const DSNode *Old); +}; + + +// ArgDSNode - Represent an incoming argument to the current function... +// +class ArgDSNode : public DSNode { + FunctionArgument *FuncArg; +public: + ArgDSNode(FunctionArgument *MA); + virtual std::string getCaption() const; + + // Support type inquiry through isa, cast, and dyn_cast... + static bool classof(const ArgDSNode *) { return true; } + static bool classof(const DSNode *N) { return N->NodeType == ArgNode; } +private: + virtual ArgDSNode *cloneImpl() const { return new ArgDSNode(FuncArg); } +}; + + +// ShadowDSNode - Represent a chunk of memory that we need to be able to +// address. These are generated due to (for example) pointer type method +// arguments... if the pointer is dereferenced, we need to have a node to point +// to. When functions are integrated into each other, shadow nodes are +// resolved. +// +class ShadowDSNode : public DSNode { + friend class FunctionDSGraph; + DSNode *Parent; + Module *Mod; + ShadowDSNode *ShadowParent; // Nonnull if this is a synthesized node... + std::vector > SynthNodes; +public: + ShadowDSNode(DSNode *Parent, Module *M); + virtual std::string getCaption() const; + + // synthesizeNode - Create a new shadow node that is to be linked into this + // chain.. + // + ShadowDSNode *synthesizeNode(const Type *Ty, FunctionRepBuilder *Rep); + + // Support type inquiry through isa, cast, and dyn_cast... + static bool classof(const ShadowDSNode *) { return true; } + static bool classof(const DSNode *N) { return N->NodeType == ShadowNode; } + +private: + ShadowDSNode(const Type *Ty, Module *M, ShadowDSNode *ShadParent); +protected: + virtual void mapNode(std::map &NodeMap, + const DSNode *Old); + virtual ShadowDSNode *cloneImpl() const { + if (ShadowParent) + return new ShadowDSNode(getType(), Mod, ShadowParent); + else + return new ShadowDSNode(Parent, Mod); + } +}; + + +// FunctionDSGraph - The graph that represents a method. +// +class FunctionDSGraph { + Function *Func; + std::vector Nodes; + std::vector ShadowNodes; + PointerValSet RetNode; // Node that gets returned... + std::map ValueMap; + + // cloneFunctionIntoSelf - Clone the specified method graph into the current + // method graph, returning the Return's set of the graph. If ValueMap is set + // to true, the ValueMap of the function is cloned into this function as well + // as the data structure graph itself. + // + PointerValSet cloneFunctionIntoSelf(const FunctionDSGraph &G, bool ValueMap); + void RemoveUnreachableShadowNodes(); + void UnlinkUndistinguishableShadowNodes(); +public: + FunctionDSGraph(Function *F); + FunctionDSGraph(const FunctionDSGraph &DSG); + ~FunctionDSGraph(); + + void computeClosure(const DataStructure &DS); + + Function *getFunction() const { return Func; } + + void printFunction(std::ostream &O, const char *Label) const; +}; + + +// FIXME: This should be a FunctionPass. When the pass framework sees a 'Pass' +// that uses the output of a FunctionPass, it should automatically build a map +// of output from the method pass that the pass can use. +// +class DataStructure : public Pass { + // DSInfo, one intraprocedural and one closed graph for each method... + typedef std::map > InfoMap; + mutable InfoMap DSInfo; +public: + static AnalysisID ID; // DataStructure Analysis ID + + DataStructure(AnalysisID id) { assert(id == ID); } + ~DataStructure() { releaseMemory(); } + + // run - Do nothing, because methods are analyzed lazily + virtual bool run(Module *TheModule) { return false; } + + // getDSGraph - Return the data structure graph for the specified method. + // Since method graphs are lazily computed, we may have to create one on the + // fly here. + // + FunctionDSGraph &getDSGraph(Function *F) const { + std::pair &N = DSInfo[F]; + if (N.first) return *N.first; + return *(N.first = new FunctionDSGraph(F)); + } + + // getClosedDSGraph - Return the data structure graph for the specified + // method. Since method graphs are lazily computed, we may have to create one + // on the fly here. This is different than the normal DSGraph for the method + // because any function calls that are resolvable will have the data structure + // graphs of the called function incorporated into this function as well. + // + FunctionDSGraph &getClosedDSGraph(Function *F) const { + std::pair &N = DSInfo[F]; + if (N.second) return *N.second; + N.second = new FunctionDSGraph(getDSGraph(F)); + N.second->computeClosure(*this); + return *N.second; + } + + // print - Print out the analysis results... + void print(std::ostream &O, Module *M) const; + + // If the pass pipeline is done with this pass, we can release our memory... + virtual void releaseMemory(); + + // getAnalysisUsageInfo - This obviously provides a call graph + virtual void getAnalysisUsageInfo(AnalysisSet &Required, + AnalysisSet &Destroyed, + AnalysisSet &Provided) { + Provided.push_back(ID); + } +}; + +#endif diff --git a/include/llvm/Analysis/DataStructure/DataStructure.h b/include/llvm/Analysis/DataStructure/DataStructure.h new file mode 100644 index 00000000000..d84530313e8 --- /dev/null +++ b/include/llvm/Analysis/DataStructure/DataStructure.h @@ -0,0 +1,377 @@ +//===- DataStructure.h - Build a Module's call graph -------------*- 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 + +class Type; +class CallInst; +class AllocationInst; +class FunctionArgument; +class DSNode; +class FunctionRepBuilder; +class GlobalValue; +class FunctionDSGraph; +class DataStructure; + +// FIXME: move this somewhere private +unsigned countPointerFields(const Type *Ty); + +// PointerVal - Represent a pointer to a datastructure. The pointer points to +// a node, and can index into it. This is used for getelementptr instructions, +// which do not affect which node a pointer points to, but does change the field +// index +// +struct PointerVal { + DSNode *Node; + unsigned Index; // Index into Node->FieldLinks[] +public: + PointerVal(DSNode *N, unsigned Idx = 0) : Node(N), Index(Idx) {} + + DSNode *getNode() const { return Node; } + unsigned getIndex() const { return Index; } + + inline bool operator==(DSNode *N) const { return Node == N; } + inline bool operator!=(DSNode *N) const { return Node != N; } + + inline bool operator==(const PointerVal &PV) const { + return Node == PV.Node && Index == PV.Index; + } + inline bool operator!=(const PointerVal &PV) const { return !operator==(PV); } + + void print(std::ostream &O) const; +}; + + +// PointerValSet - This class represents a list of pointer values. The add +// method is used to add values to the set, and ensures that duplicates cannot +// happen. +// +class PointerValSet { + std::vector Vals; + void dropRefs(); + void addRefs(); +public: + PointerValSet() {} + PointerValSet(const PointerValSet &PVS) : Vals(PVS.Vals) { addRefs(); } + ~PointerValSet() { dropRefs(); } + const PointerValSet &operator=(const PointerValSet &PVS); + + const PointerVal &operator[](unsigned i) const { return Vals[i]; } + + unsigned size() const { return Vals.size(); } + bool empty() const { return Vals.empty(); } + void clear() { dropRefs(); Vals.clear(); } + + // add - Add the specified pointer, or contents of the specified PVS to this + // pointer set. If a 'Pointer' value is provided, notify the underlying data + // structure node that the pointer is pointing to it, so that it can be + // invalidated if neccesary later. True is returned if the value is new to + // this pointer. + // + bool add(const PointerVal &PV, Value *Pointer = 0); + bool add(const PointerValSet &PVS, Value *Pointer = 0) { + bool Changed = false; + for (unsigned i = 0, e = PVS.size(); i != e; ++i) + Changed |= add(PVS[i], Pointer); + return Changed; + } + + // removePointerTo - Remove a single pointer val that points to the specified + // node... + void removePointerTo(DSNode *Node); + + void print(std::ostream &O) const; +}; + + +//===----------------------------------------------------------------------===// +// DSNode - Base class for all data structure nodes... +// +// This class keeps track of its type, the pointer fields in the data structure, +// and a list of LLVM values that are pointing to this node. +// +class DSNode { + friend class FunctionDSGraph; + const Type *Ty; + std::vector FieldLinks; + std::vector Pointers; // Values pointing to me... + std::vector Referrers; + + DSNode(const DSNode &); // DO NOT IMPLEMENT + void operator=(const DSNode &); // DO NOT IMPLEMENT +public: + enum NodeTy { + NewNode, CallNode, ShadowNode, ArgNode, GlobalNode + } NodeType; + + DSNode(enum NodeTy NT, const Type *T); + virtual ~DSNode() { + dropAllReferences(); + assert(Referrers.empty() && "Referrers to dead node exist!"); + } + + unsigned getNumLinks() const { return FieldLinks.size(); } + PointerValSet &getLink(unsigned i) { + assert(i < getNumLinks() && "Field links access out of range..."); + return FieldLinks[i]; + } + + // addReferrer - Keep the referrer set up to date... + void addReferrer(PointerValSet *PVS) { Referrers.push_back(PVS); } + void removeReferrer(PointerValSet *PVS); + const std::vector &getReferrers() const { return Referrers; } + + void addPointer(Value *V) { Pointers.push_back(V); } + const std::vector &getPointers() const { return Pointers; } + + const Type *getType() const { return Ty; } + + void print(std::ostream &O) const; + + virtual std::string getCaption() const = 0; + virtual const std::vector *getAuxLinks() const { + return 0; // Default to nothing... + } + + DSNode *clone() const { + DSNode *New = cloneImpl(); + // Add all of the pointers to the new node... + for (unsigned pn = 0, pe = Pointers.size(); pn != pe; ++pn) + New->addPointer(Pointers[pn]); + return New; + } + + + virtual void dropAllReferences() { + FieldLinks.clear(); + } + +protected: + virtual DSNode *cloneImpl() const = 0; + virtual void mapNode(std::map &NodeMap, + const DSNode *Old); +}; + + +// NewDSNode - Represent all allocation (malloc or alloca) in the program. +// +class NewDSNode : public DSNode { + AllocationInst *Allocation; +public: + NewDSNode(AllocationInst *V); + + virtual std::string getCaption() const; + + // Support type inquiry through isa, cast, and dyn_cast... + static bool classof(const NewDSNode *) { return true; } + static bool classof(const DSNode *N) { return N->NodeType == NewNode; } +protected: + virtual NewDSNode *cloneImpl() const { return new NewDSNode(Allocation); } +}; + + +// GlobalDSNode - Represent the memory location that a global variable occupies +// +class GlobalDSNode : public DSNode { + GlobalValue *Val; +public: + GlobalDSNode(GlobalValue *V); + + virtual std::string getCaption() const; + + // Support type inquiry through isa, cast, and dyn_cast... + static bool classof(const GlobalDSNode *) { return true; } + static bool classof(const DSNode *N) { return N->NodeType == GlobalNode; } +private: + virtual GlobalDSNode *cloneImpl() const { return new GlobalDSNode(Val); } +}; + + +// CallDSNode - Represent a call instruction in the program... +// +class CallDSNode : public DSNode { + CallInst *CI; + std::vector ArgLinks; +public: + CallDSNode(CallInst *CI); + + CallInst *getCall() const { return CI; } + + const std::vector *getAuxLinks() const { return &ArgLinks; } + virtual std::string getCaption() const; + + bool addArgValue(unsigned ArgNo, const PointerValSet &PVS) { + return ArgLinks[ArgNo].add(PVS); + } + + unsigned getNumArgs() const { return ArgLinks.size(); } + const PointerValSet &getArgValues(unsigned ArgNo) const { + assert(ArgNo < ArgLinks.size() && "Arg # out of range!"); + return ArgLinks[ArgNo]; + } + + virtual void dropAllReferences() { + DSNode::dropAllReferences(); + ArgLinks.clear(); + } + + + // Support type inquiry through isa, cast, and dyn_cast... + static bool classof(const CallDSNode *) { return true; } + static bool classof(const DSNode *N) { return N->NodeType == CallNode; } +private: + virtual CallDSNode *cloneImpl() const { return new CallDSNode(CI); } + virtual void mapNode(std::map &NodeMap, + const DSNode *Old); +}; + + +// ArgDSNode - Represent an incoming argument to the current function... +// +class ArgDSNode : public DSNode { + FunctionArgument *FuncArg; +public: + ArgDSNode(FunctionArgument *MA); + virtual std::string getCaption() const; + + // Support type inquiry through isa, cast, and dyn_cast... + static bool classof(const ArgDSNode *) { return true; } + static bool classof(const DSNode *N) { return N->NodeType == ArgNode; } +private: + virtual ArgDSNode *cloneImpl() const { return new ArgDSNode(FuncArg); } +}; + + +// ShadowDSNode - Represent a chunk of memory that we need to be able to +// address. These are generated due to (for example) pointer type method +// arguments... if the pointer is dereferenced, we need to have a node to point +// to. When functions are integrated into each other, shadow nodes are +// resolved. +// +class ShadowDSNode : public DSNode { + friend class FunctionDSGraph; + DSNode *Parent; + Module *Mod; + ShadowDSNode *ShadowParent; // Nonnull if this is a synthesized node... + std::vector > SynthNodes; +public: + ShadowDSNode(DSNode *Parent, Module *M); + virtual std::string getCaption() const; + + // synthesizeNode - Create a new shadow node that is to be linked into this + // chain.. + // + ShadowDSNode *synthesizeNode(const Type *Ty, FunctionRepBuilder *Rep); + + // Support type inquiry through isa, cast, and dyn_cast... + static bool classof(const ShadowDSNode *) { return true; } + static bool classof(const DSNode *N) { return N->NodeType == ShadowNode; } + +private: + ShadowDSNode(const Type *Ty, Module *M, ShadowDSNode *ShadParent); +protected: + virtual void mapNode(std::map &NodeMap, + const DSNode *Old); + virtual ShadowDSNode *cloneImpl() const { + if (ShadowParent) + return new ShadowDSNode(getType(), Mod, ShadowParent); + else + return new ShadowDSNode(Parent, Mod); + } +}; + + +// FunctionDSGraph - The graph that represents a method. +// +class FunctionDSGraph { + Function *Func; + std::vector Nodes; + std::vector ShadowNodes; + PointerValSet RetNode; // Node that gets returned... + std::map ValueMap; + + // cloneFunctionIntoSelf - Clone the specified method graph into the current + // method graph, returning the Return's set of the graph. If ValueMap is set + // to true, the ValueMap of the function is cloned into this function as well + // as the data structure graph itself. + // + PointerValSet cloneFunctionIntoSelf(const FunctionDSGraph &G, bool ValueMap); + void RemoveUnreachableShadowNodes(); + void UnlinkUndistinguishableShadowNodes(); +public: + FunctionDSGraph(Function *F); + FunctionDSGraph(const FunctionDSGraph &DSG); + ~FunctionDSGraph(); + + void computeClosure(const DataStructure &DS); + + Function *getFunction() const { return Func; } + + void printFunction(std::ostream &O, const char *Label) const; +}; + + +// FIXME: This should be a FunctionPass. When the pass framework sees a 'Pass' +// that uses the output of a FunctionPass, it should automatically build a map +// of output from the method pass that the pass can use. +// +class DataStructure : public Pass { + // DSInfo, one intraprocedural and one closed graph for each method... + typedef std::map > InfoMap; + mutable InfoMap DSInfo; +public: + static AnalysisID ID; // DataStructure Analysis ID + + DataStructure(AnalysisID id) { assert(id == ID); } + ~DataStructure() { releaseMemory(); } + + // run - Do nothing, because methods are analyzed lazily + virtual bool run(Module *TheModule) { return false; } + + // getDSGraph - Return the data structure graph for the specified method. + // Since method graphs are lazily computed, we may have to create one on the + // fly here. + // + FunctionDSGraph &getDSGraph(Function *F) const { + std::pair &N = DSInfo[F]; + if (N.first) return *N.first; + return *(N.first = new FunctionDSGraph(F)); + } + + // getClosedDSGraph - Return the data structure graph for the specified + // method. Since method graphs are lazily computed, we may have to create one + // on the fly here. This is different than the normal DSGraph for the method + // because any function calls that are resolvable will have the data structure + // graphs of the called function incorporated into this function as well. + // + FunctionDSGraph &getClosedDSGraph(Function *F) const { + std::pair &N = DSInfo[F]; + if (N.second) return *N.second; + N.second = new FunctionDSGraph(getDSGraph(F)); + N.second->computeClosure(*this); + return *N.second; + } + + // print - Print out the analysis results... + void print(std::ostream &O, Module *M) const; + + // If the pass pipeline is done with this pass, we can release our memory... + virtual void releaseMemory(); + + // getAnalysisUsageInfo - This obviously provides a call graph + virtual void getAnalysisUsageInfo(AnalysisSet &Required, + AnalysisSet &Destroyed, + AnalysisSet &Provided) { + Provided.push_back(ID); + } +}; + +#endif -- 2.34.1