From c68c31b2d3623f01bfcd1405ec005536d0815970 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Wed, 10 Jul 2002 22:38:08 +0000 Subject: [PATCH] New implementation of data structure analysis. Only local analysis has been implemented so far. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@2871 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Analysis/DataStructure/DataStructure.cpp | 222 ++++++--------- lib/Analysis/DataStructure/Local.cpp | 283 +++++++++++++++++++ lib/Analysis/DataStructure/Printer.cpp | 156 ++++++++++ 3 files changed, 531 insertions(+), 130 deletions(-) create mode 100644 lib/Analysis/DataStructure/Local.cpp create mode 100644 lib/Analysis/DataStructure/Printer.cpp diff --git a/lib/Analysis/DataStructure/DataStructure.cpp b/lib/Analysis/DataStructure/DataStructure.cpp index 7575a5da5bc..84e2cfbc47e 100644 --- a/lib/Analysis/DataStructure/DataStructure.cpp +++ b/lib/Analysis/DataStructure/DataStructure.cpp @@ -1,165 +1,127 @@ -//===- DataStructure.cpp - Analysis for data structure identification -------=// +//===- DataStructure.cpp - Implement the core data structure analysis -----===// // -// Implement the LLVM data structure analysis library. +// This file implements the core data structure functionality. // //===----------------------------------------------------------------------===// #include "llvm/Analysis/DataStructure.h" #include "llvm/Module.h" -#include +#include "llvm/DerivedTypes.h" #include +#include "Support/STLExtras.h" -//===----------------------------------------------------------------------===// -// DataStructure Class Implementation -// +AnalysisID LocalDataStructures::ID(AnalysisID::create()); -AnalysisID DataStructure::ID(AnalysisID::create()); +//===----------------------------------------------------------------------===// +// DSNode Implementation +//===----------------------------------------------------------------------===// -// releaseMemory - If the pass pipeline is done with this pass, we can release -// our memory... here... -void DataStructure::releaseMemory() { - for (InfoMap::iterator I = DSInfo.begin(), E = DSInfo.end(); I != E; ++I) { - delete I->second.first; - delete I->second.second; +DSNode::DSNode(enum NodeTy NT, const Type *T) : Ty(T), NodeType(NT) { + // If this node has any fields, allocate them now, but leave them null. + switch (T->getPrimitiveID()) { + case Type::PointerTyID: Links.resize(1); break; + case Type::ArrayTyID: Links.resize(1); break; + case Type::StructTyID: + Links.resize(cast(T)->getNumContainedTypes()); + break; + default: break; } +} - // Empty map so next time memory is released, data structures are not - // re-deleted. - DSInfo.clear(); +void DSNode::removeReferrer(DSNodeHandle *H) { + // Search backwards, because we depopulate the list from the back for + // efficiency (because it's a vector). + std::vector::reverse_iterator I = + std::find(Referrers.rbegin(), Referrers.rend(), H); + assert(I != Referrers.rend() && "Referrer not pointing to node!"); + Referrers.erase(I.base()-1); } -// FIXME REMOVE -#include -#include "Support/CommandLine.h" - -cl::Flag Time("t", "Print analysis time..."); - - -// print - Print out the analysis results... -void DataStructure::print(std::ostream &O, Module *M) const { - if (Time) { - timeval TV1, TV2; - gettimeofday(&TV1, 0); - for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I) - if (!I->isExternal() && I->getName() == "main") { - //getDSGraph(*I); - getClosedDSGraph(I); - } - gettimeofday(&TV2, 0); - std::cerr << "Analysis took " - << (TV2.tv_sec-TV1.tv_sec)*1000000+(TV2.tv_usec-TV1.tv_usec) - << " microseconds.\n"; +// 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 LinkNo, DSNode *N) { + assert(LinkNo < Links.size() && "LinkNo out of range!"); + if (N == 0 || Links[LinkNo] == N) return; // Nothing to do + if (Links[LinkNo] == 0) { // No merging to perform + Links[LinkNo] = N; + return; } - for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I) - if (!I->isExternal()) { - - std::string Filename = "ds." + I->getName() + ".dot"; - O << "Writing '" << Filename << "'..."; - std::ofstream F(Filename.c_str()); - - if (F.good()) { - F << "digraph DataStructures {\n" - << "\tnode [shape=Mrecord];\n" - << "\tedge [arrowtail=\"dot\"];\n" - << "\tsize=\"10,7.5\";\n" - << "\trotate=\"90\";\n"; - - getDSGraph(I).printFunction(F, "Local"); - getClosedDSGraph(I).printFunction(F, "Closed"); - - F << "}\n"; - } else { - O << " error opening file for writing!\n"; - } - - if (Time) - O << " [" << getDSGraph(I).getGraphSize() << ", " - << getClosedDSGraph(I).getGraphSize() << "]\n"; - else - O << "\n"; - } + // Merge the two nodes... + Links[LinkNo]->mergeWith(N); } -//===----------------------------------------------------------------------===// -// PointerVal Class Implementation +// mergeWith - Merge this node into the specified node, moving all links to and +// from the argument node into the current node. The specified node may be a +// null pointer (in which case, nothing happens). // - -void PointerVal::print(std::ostream &O) const { - if (Node) { - O << " Node: " << Node->getCaption() << "[" << Index << "]\n"; - } else { - O << " NULL NODE\n"; +void DSNode::mergeWith(DSNode *N) { + if (N == 0 || N == this) return; // Noop + assert(N->Ty == Ty && N->Links.size() == Links.size() && + "Cannot merge nodes of two different types!"); + + // Remove all edges pointing at N, causing them to point to 'this' instead. + while (!N->Referrers.empty()) + *N->Referrers.back() = this; + + // Make all of the outgoing links of N now be outgoing links of this. This + // can cause recursive merging! + // + for (unsigned i = 0, e = Links.size(); i != e; ++i) { + addEdgeTo(i, N->Links[i]); + N->Links[i] = 0; // Reduce unneccesary edges in graph. N is dead } -} - -//===----------------------------------------------------------------------===// -// PointerValSet Class Implementation -// -void PointerValSet::addRefs() { - for (unsigned i = 0, e = Vals.size(); i != e; ++i) - Vals[i].Node->addReferrer(this); + NodeType |= N->NodeType; + N->NodeType = 0; // N is now a dead node. } -void PointerValSet::dropRefs() { - for (unsigned i = 0, e = Vals.size(); i != e; ++i) - Vals[i].Node->removeReferrer(this); -} - -const PointerValSet &PointerValSet::operator=(const PointerValSet &PVS) { - dropRefs(); - Vals.clear(); - Vals = PVS.Vals; - addRefs(); - return *this; -} - -// operator< - Allow insertion into a map... -bool PointerValSet::operator<(const PointerValSet &PVS) const { - if (Vals.size() < PVS.Vals.size()) return true; - if (Vals.size() > PVS.Vals.size()) return false; - if (Vals.size() == 1) return Vals[0] < PVS.Vals[0]; // Most common case +//===----------------------------------------------------------------------===// +// DSGraph Implementation +//===----------------------------------------------------------------------===// - std::vector S1(Vals), S2(PVS.Vals); - sort(S1.begin(), S1.end()); - sort(S2.begin(), S2.end()); - return S1 < S2; -} +DSGraph::~DSGraph() { + FunctionCalls.clear(); + ValueMap.clear(); + RetNode = 0; -bool PointerValSet::operator==(const PointerValSet &PVS) const { - if (Vals.size() != PVS.Vals.size()) return false; - if (Vals.size() == 1) return Vals[0] == PVS.Vals[0]; // Most common case... +#ifndef NDEBUG + // Drop all intra-node references, so that assertions don't fail... + std::for_each(Nodes.begin(), Nodes.end(), + std::mem_fun(&DSNode::dropAllReferences)); +#endif - std::vector S1(Vals), S2(PVS.Vals); - sort(S1.begin(), S1.end()); - sort(S2.begin(), S2.end()); - return S1 == S2; + // Delete all of the nodes themselves... + std::for_each(Nodes.begin(), Nodes.end(), deleter); } +//===----------------------------------------------------------------------===// +// LocalDataStructures Implementation +//===----------------------------------------------------------------------===// -bool PointerValSet::add(const PointerVal &PV, Value *Pointer) { - if (std::find(Vals.begin(), Vals.end(), PV) != Vals.end()) - return false; - Vals.push_back(PV); - if (Pointer) PV.Node->addPointer(Pointer); - PV.Node->addReferrer(this); - return true; -} +// releaseMemory - If the pass pipeline is done with this pass, we can release +// our memory... here... +// +void LocalDataStructures::releaseMemory() { + for (std::map::iterator I = DSInfo.begin(), + E = DSInfo.end(); I != E; ++I) + delete I->second; -// removePointerTo - Remove a single pointer val that points to the specified -// node... -void PointerValSet::removePointerTo(DSNode *Node) { - std::vector::iterator I = std::find(Vals.begin(), Vals.end(), Node); - assert(I != Vals.end() && "Couldn't remove nonexistent edge!"); - Vals.erase(I); - Node->removeReferrer(this); + // Empty map so next time memory is released, data structures are not + // re-deleted. + DSInfo.clear(); } +bool LocalDataStructures::run(Module &M) { + // Calculate all of the graphs... + for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) + if (!I->isExternal()) { + std::map::iterator DI = DSInfo.find(I); + if (DI == DSInfo.end() || DI->second == 0) + DSInfo.insert(std::make_pair(&*I, new DSGraph(*I))); + } -void PointerValSet::print(std::ostream &O) const { - for (unsigned i = 0, e = Vals.size(); i != e; ++i) - Vals[i].print(O); + return false; } - diff --git a/lib/Analysis/DataStructure/Local.cpp b/lib/Analysis/DataStructure/Local.cpp new file mode 100644 index 00000000000..87767ce4178 --- /dev/null +++ b/lib/Analysis/DataStructure/Local.cpp @@ -0,0 +1,283 @@ +//===- ComputeLocal.cpp - Compute a local data structure graph for a fn ---===// +// +// 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/Function.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/Support/InstVisitor.h" +using std::map; +using std::vector; + +//===----------------------------------------------------------------------===// +// 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. +// + +namespace { + class GraphBuilder : InstVisitor { + DSGraph &G; + vector &Nodes; + DSNodeHandle &RetNode; // Node that gets returned... + map &ValueMap; + vector > &FunctionCalls; + + public: + GraphBuilder(DSGraph &g, vector &nodes, DSNodeHandle &retNode, + map &vm, + vector > &fc) + : G(g), Nodes(nodes), RetNode(retNode), ValueMap(vm), FunctionCalls(fc) { + visit(G.getFunction()); // Single pass over the function + removeDeadNodes(); + } + + private: + // Visitor functions, used to handle each instruction type we encounter... + friend class InstVisitor; + void visitMallocInst(MallocInst &MI) { handleAlloc(MI, DSNode::NewNode); } + void visitAllocaInst(AllocaInst &AI) { handleAlloc(AI, DSNode::AllocaNode);} + void handleAlloc(AllocationInst &AI, DSNode::NodeTy NT); + + void visitPHINode(PHINode &PN); + + void visitGetElementPtrInst(GetElementPtrInst &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) {} // Ignore free instructions + void visitInstruction(Instruction &I) { +#ifndef NDEBUG + bool bad = isa(I.getType()); + for (Instruction::op_iterator i = I.op_begin(), E = I.op_end(); i!=E; ++i) + bad |= isa(i->get()->getType()); + if (bad) { + std::cerr << "\n\n\nUNKNOWN PTR INSTRUCTION type: " << I << "\n\n\n"; + assert(0 && "Cannot proceed"); + } +#endif + } + + 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(DSNode::NodeTy NodeType, const Type *Ty); + + // getValueNode - Return a DSNode that corresponds the the specified LLVM + // value. This either returns the already existing node, or creates a new + // one and adds it to the graph, if none exists. + // + DSNode *getValueNode(Value &V); + + // getLink - This method is used to either 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. + // + DSNode *getLink(DSNode *Node, unsigned Link); + + // getSubscriptedNode - Perform the basic getelementptr functionality that + // must be factored out of gep, load and store while they are all MAI's. + // + DSNode *getSubscriptedNode(MemAccessInst &MAI, DSNode *Ptr); + + // removeDeadNodes - 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. + // + void removeDeadNodes(); + }; +} + +//===----------------------------------------------------------------------===// +// DSGraph constructor - Simply use the GraphBuilder to construct the local +// graph. +DSGraph::DSGraph(Function &F) : Func(F), RetNode(0) { + // Use the graph builder to construct the local version of the graph + GraphBuilder B(*this, Nodes, RetNode, ValueMap, FunctionCalls); +} + + +//===----------------------------------------------------------------------===// +// Helper method implementations... +// + + +// createNode - Create a new DSNode, ensuring that it is properly added to the +// graph. +// +DSNode *GraphBuilder::createNode(DSNode::NodeTy NodeType, const Type *Ty) { + DSNode *N = new DSNode(NodeType, Ty); + Nodes.push_back(N); + return N; +} + + +// getValueNode - Return a DSNode that corresponds the the specified LLVM value. +// This either returns the already existing node, or creates a new one and adds +// it to the graph, if none exists. +// +DSNode *GraphBuilder::getValueNode(Value &V) { + assert(isa(V.getType()) && "Should only use pointer scalars!"); + DSNodeHandle &N = ValueMap[&V]; + if (N) return N; // Already have a node? Just return it... + + // Otherwise we need to create a new scalar node... + N = createNode(DSNode::ScalarNode, V.getType()); + + if (isa(V)) { + // Traverse the global graph, adding nodes for them all, and marking them + // all globals. Be careful to mark functions global as well as the + // potential graph of global variables. + // + DSNode *G = getLink(N, 0); + G->NodeType |= DSNode::GlobalNode; + } + + return N; +} + +// getLink - This method is used to either 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. +// +DSNode *GraphBuilder::getLink(DSNode *Node, unsigned Link) { + assert(Link < Node->getNumLinks() && "Link accessed out of range!"); + if (Node->getLink(Link) == 0) { + DSNode::NodeTy NT; + const Type *Ty; + + switch (Node->getType()->getPrimitiveID()) { + case Type::PointerTyID: + Ty = cast(Node->getType())->getElementType(); + NT = DSNode::ShadowNode; + break; + case Type::ArrayTyID: + Ty = cast(Node->getType())->getElementType(); + NT = DSNode::SubElement; + break; + case Type::StructTyID: + Ty = cast(Node->getType())->getContainedType(Link); + NT = DSNode::SubElement; + break; + default: + assert(0 && "Unexpected type to dereference!"); + abort(); + } + + DSNode *New = createNode(NT, Ty); + Node->addEdgeTo(Link, New); + } + + return Node->getLink(Link); +} + +// getSubscriptedNode - Perform the basic getelementptr functionality that must +// be factored out of gep, load and store while they are all MAI's. +// +DSNode *GraphBuilder::getSubscriptedNode(MemAccessInst &MAI, DSNode *Ptr) { + for (unsigned i = MAI.getFirstIndexOperandNumber(), e = MAI.getNumOperands(); + i != e; ++i) + if (MAI.getOperand(i)->getType() == Type::UIntTy) + Ptr = getLink(Ptr, 0); + else if (MAI.getOperand(i)->getType() == Type::UByteTy) + Ptr = getLink(Ptr, cast(MAI.getOperand(i))->getValue()); + + if (MAI.getFirstIndexOperandNumber() == MAI.getNumOperands()) + Ptr = getLink(Ptr, 0); // All MAI's have an implicit 0 if nothing else. + + return Ptr; +} + + +// removeDeadNodes - 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 GraphBuilder::removeDeadNodes() { + for (unsigned i = 0; i != Nodes.size(); ) + if (!Nodes[i]->getReferrers().empty()) + ++i; // This node is alive! + else { // This node is dead! + delete Nodes[i]; // Free memory... + Nodes.erase(Nodes.begin()+i); // Remove from node list... + } +} + + + + +//===----------------------------------------------------------------------===// +// 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, DSNode::NodeTy NodeType) { + DSNode *Scalar = getValueNode(AI); + DSNode *New = createNode(NodeType, AI.getAllocatedType()); + Scalar->addEdgeTo(New); // Make the scalar point to the new node... +} + +// 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 (!isa(PN.getType())) return; // Only pointer PHIs + + DSNode *Scalar = getValueNode(PN); + for (unsigned i = 0, e = PN.getNumIncomingValues(); i != e; ++i) + Scalar->mergeWith(getValueNode(*PN.getIncomingValue(i))); +} + +void GraphBuilder::visitGetElementPtrInst(GetElementPtrInst &GEP) { + DSNode *Scalar = getValueNode(GEP); + DSNode *Ptr = getSubscriptedNode(GEP, getValueNode(*GEP.getOperand(0))); + Scalar->addEdgeTo(Ptr); +} + +void GraphBuilder::visitLoadInst(LoadInst &LI) { + if (!isa(LI.getType())) return; // Only pointer PHIs + DSNode *Ptr = getSubscriptedNode(LI, getValueNode(*LI.getOperand(0))); + getValueNode(LI)->mergeWith(Ptr); +} + +void GraphBuilder::visitStoreInst(StoreInst &SI) { + if (!isa(SI.getOperand(0)->getType())) return; + DSNode *Value = getValueNode(*SI.getOperand(0)); + DSNode *DestPtr = getValueNode(*SI.getOperand(1)); + Value->mergeWith(getSubscriptedNode(SI, DestPtr)); +} + +void GraphBuilder::visitReturnInst(ReturnInst &RI) { + if (RI.getNumOperands() && isa(RI.getOperand(0)->getType())) { + DSNode *Value = getValueNode(*RI.getOperand(0)); + Value->mergeWith(RetNode); + RetNode = Value; + } +} + +void GraphBuilder::visitCallInst(CallInst &CI) { + FunctionCalls.push_back(vector()); + vector &Args = FunctionCalls.back(); + + for (unsigned i = 0, e = CI.getNumOperands(); i != e; ++i) + if (isa(CI.getOperand(i)->getType())) + Args.push_back(getValueNode(*CI.getOperand(i))); +} diff --git a/lib/Analysis/DataStructure/Printer.cpp b/lib/Analysis/DataStructure/Printer.cpp new file mode 100644 index 00000000000..c0caaa07d75 --- /dev/null +++ b/lib/Analysis/DataStructure/Printer.cpp @@ -0,0 +1,156 @@ +//===- Printer.cpp - Code for printing data structure graphs nicely -------===// +// +// This file implements the 'dot' graph printer. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Analysis/DataStructure.h" +#include "llvm/Module.h" +#include "llvm/Assembly/Writer.h" +#include +#include + +void DSNode::dump() const { print(std::cerr, 0); } + +std::string DSNode::getCaption(Function *F) const { + std::stringstream OS; + WriteTypeSymbolic(OS, getType(), F ? F->getParent() : 0); + + OS << " "; + if (NodeType & ScalarNode) OS << "S"; + if (NodeType & AllocaNode) OS << "A"; + if (NodeType & NewNode ) OS << "N"; + if (NodeType & GlobalNode) OS << "G"; + if (NodeType & SubElement) OS << "E"; + if (NodeType & CastNode ) OS << "C"; + + return OS.str(); +} + +static std::string getValueName(Value *V, Function &F) { + std::stringstream OS; + WriteAsOperand(OS, V, true, true, F.getParent()); + return OS.str(); +} + + + +static void replaceIn(std::string &S, char From, const std::string &To) { + for (unsigned i = 0; i < S.size(); ) + if (S[i] == From) { + S.replace(S.begin()+i, S.begin()+i+1, + To.begin(), To.end()); + i += To.size(); + } else { + ++i; + } +} + +static string escapeLabel(const string &In) { + string Label(In); + replaceIn(Label, '\\', "\\\\"); // Escape caption... + replaceIn(Label, ' ', "\\ "); + replaceIn(Label, '{', "\\{"); + replaceIn(Label, '}', "\\}"); + return Label; +} + +static void writeEdge(std::ostream &O, const void *SrcNode, + const char *SrcNodePortName, int SrcNodeIdx, + const DSNode *VS, const string &EdgeAttr = "") { + O << "\tNode" << SrcNode << SrcNodePortName; + if (SrcNodeIdx != -1) O << SrcNodeIdx; + O << " -> Node" << (void*)VS; + + if (!EdgeAttr.empty()) + O << "[" << EdgeAttr << "]"; + O << ";\n"; +} + +void DSNode::print(std::ostream &O, Function *F) const { + string Caption = escapeLabel(getCaption(F)); + + O << "\tNode" << (void*)this << " [ label =\"{" << Caption; + + if (!Links.empty()) { + O << "|{"; + for (unsigned i = 0; i < Links.size(); ++i) { + if (i) O << "|"; + O << ""; + } + O << "}"; + } + O << "}\"];\n"; + + for (unsigned i = 0; i < Links.size(); ++i) + if (Links[i]) + writeEdge(O, this, ":g", i, Links[i]); +} + +void DSGraph::print(std::ostream &O) const { + O << "digraph DataStructures {\n" + << "\tnode [shape=Mrecord];\n" + << "\tedge [arrowtail=\"dot\"];\n" + << "\tsize=\"10,7.5\";\n" + << "\trotate=\"90\";\n" + << "\tlabel=\"Function\\ " << Func.getName() << "\";\n\n"; + + // Output all of the nodes... + for (unsigned i = 0, e = Nodes.size(); i != e; ++i) + Nodes[i]->print(O, &Func); + + O << "\n"; + // Output all of the nodes edges for scalar labels + for (std::map::const_iterator I = ValueMap.begin(), + E = ValueMap.end(); I != E; ++I) { + O << "\tNode" << (void*)I->first << "[ shape=circle, label =\"" + << escapeLabel(getValueName(I->first, Func)) << "\",style=dotted];\n"; + writeEdge(O, I->first, "",-1, I->second.get(),"arrowtail=tee,style=dotted"); + } + + // Output the returned value pointer... + if (RetNode != 0) { + O << "\tNode0x1" << "[ shape=circle, label =\"" + << escapeLabel("Return") << "\"];\n"; + writeEdge(O, (void*)1, "", -1, RetNode, "arrowtail=tee,style=dotted"); + } + + // Output all of the call nodes... + for (unsigned i = 0, e = FunctionCalls.size(); i != e; ++i) { + const std::vector &Call = FunctionCalls[i]; + O << "\tNode" << (void*)&Call << " [shape=record,label=\"{call|{"; + for (unsigned j = 0, e = Call.size(); j != e; ++j) { + if (j) O << "|"; + O << ""; + } + O << "}}\"];\n"; + + for (unsigned j = 0, e = Call.size(); j != e; ++j) + if (Call[j]) + writeEdge(O, &Call, ":g", j, Call[j]); + } + + + O << "}\n"; +} + + + + +// print - Print out the analysis results... +void LocalDataStructures::print(std::ostream &O, Module *M) const { + for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I) + if (!I->isExternal()) { + std::string Filename = "ds." + I->getName() + ".dot"; + O << "Writing '" << Filename << "'..."; + std::ofstream F(Filename.c_str()); + + if (F.good()) { + DSGraph &Graph = getDSGraph(*I); + Graph.print(F); + O << " [" << Graph.getGraphSize() << "]\n"; + } else { + O << " error opening file for writing!\n"; + } + } +} -- 2.34.1