2002-10-01 22:33:50 +00:00
|
|
|
//===- Local.cpp - Compute a local data structure graph for a function ----===//
|
2005-04-21 21:13:18 +00:00
|
|
|
//
|
2003-10-20 19:43:21 +00:00
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
|
|
|
// This file was developed by the LLVM research group and is distributed under
|
|
|
|
// the University of Illinois Open Source License. See LICENSE.TXT for details.
|
2005-04-21 21:13:18 +00:00
|
|
|
//
|
2003-10-20 19:43:21 +00:00
|
|
|
//===----------------------------------------------------------------------===//
|
2002-07-10 22:38:08 +00:00
|
|
|
//
|
|
|
|
// Compute the local version of the data structure graph for a function. The
|
|
|
|
// external interface to this file is the DSGraph constructor.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2004-07-07 06:32:21 +00:00
|
|
|
#include "llvm/Analysis/DataStructure/DataStructure.h"
|
|
|
|
#include "llvm/Analysis/DataStructure/DSGraph.h"
|
2002-07-10 22:38:08 +00:00
|
|
|
#include "llvm/Constants.h"
|
|
|
|
#include "llvm/DerivedTypes.h"
|
2003-09-20 16:34:13 +00:00
|
|
|
#include "llvm/Instructions.h"
|
2004-02-13 16:09:54 +00:00
|
|
|
#include "llvm/Intrinsics.h"
|
2003-11-25 20:19:55 +00:00
|
|
|
#include "llvm/Support/GetElementPtrTypeIterator.h"
|
2002-07-10 22:38:08 +00:00
|
|
|
#include "llvm/Support/InstVisitor.h"
|
2002-10-01 22:33:50 +00:00
|
|
|
#include "llvm/Target/TargetData.h"
|
2004-09-01 22:55:40 +00:00
|
|
|
#include "llvm/Support/CommandLine.h"
|
|
|
|
#include "llvm/Support/Debug.h"
|
|
|
|
#include "llvm/Support/Timer.h"
|
2006-01-22 22:53:01 +00:00
|
|
|
#include <iostream>
|
2002-10-01 22:33:50 +00:00
|
|
|
|
|
|
|
// FIXME: This should eventually be a FunctionPass that is automatically
|
|
|
|
// aggregated into a Pass.
|
|
|
|
//
|
|
|
|
#include "llvm/Module.h"
|
|
|
|
|
2003-11-12 23:11:14 +00:00
|
|
|
using namespace llvm;
|
2003-11-11 22:41:34 +00:00
|
|
|
|
2002-07-27 01:12:15 +00:00
|
|
|
static RegisterAnalysis<LocalDataStructures>
|
|
|
|
X("datastructure", "Local Data Structure Analysis");
|
|
|
|
|
2003-11-13 03:10:49 +00:00
|
|
|
static cl::opt<bool>
|
2004-08-02 20:16:21 +00:00
|
|
|
TrackIntegersAsPointers("dsa-track-integers", cl::Hidden,
|
2003-11-13 03:10:49 +00:00
|
|
|
cl::desc("If this is set, track integers as potential pointers"));
|
|
|
|
|
2005-12-19 17:38:39 +00:00
|
|
|
static cl::list<std::string>
|
2005-12-19 20:14:38 +00:00
|
|
|
AllocList("dsa-alloc-list",
|
2005-12-19 17:38:39 +00:00
|
|
|
cl::value_desc("list"),
|
|
|
|
cl::desc("List of functions that allocate memory from the heap"),
|
2005-12-19 20:14:38 +00:00
|
|
|
cl::CommaSeparated, cl::Hidden);
|
2005-12-19 17:38:39 +00:00
|
|
|
|
2005-12-19 19:54:23 +00:00
|
|
|
static cl::list<std::string>
|
2005-12-19 20:14:38 +00:00
|
|
|
FreeList("dsa-free-list",
|
2005-12-19 19:54:23 +00:00
|
|
|
cl::value_desc("list"),
|
|
|
|
cl::desc("List of functions that free memory from the heap"),
|
2005-12-19 20:14:38 +00:00
|
|
|
cl::CommaSeparated, cl::Hidden);
|
2005-12-19 19:54:23 +00:00
|
|
|
|
2003-11-12 23:11:14 +00:00
|
|
|
namespace llvm {
|
2002-11-07 05:20:53 +00:00
|
|
|
namespace DS {
|
2002-10-01 22:33:50 +00:00
|
|
|
// isPointerType - Return true if this type is big enough to hold a pointer.
|
|
|
|
bool isPointerType(const Type *Ty) {
|
|
|
|
if (isa<PointerType>(Ty))
|
|
|
|
return true;
|
2003-11-13 03:10:49 +00:00
|
|
|
else if (TrackIntegersAsPointers && Ty->isPrimitiveType() &&Ty->isInteger())
|
2002-10-01 22:33:50 +00:00
|
|
|
return Ty->getPrimitiveSize() >= PointerSize;
|
|
|
|
return false;
|
|
|
|
}
|
2003-11-12 23:11:14 +00:00
|
|
|
}}
|
2002-10-01 22:33:50 +00:00
|
|
|
|
2003-11-11 22:41:34 +00:00
|
|
|
using namespace DS;
|
2002-07-10 22:38:08 +00:00
|
|
|
|
|
|
|
namespace {
|
2003-02-05 21:59:58 +00:00
|
|
|
cl::opt<bool>
|
|
|
|
DisableDirectCallOpt("disable-direct-call-dsopt", cl::Hidden,
|
|
|
|
cl::desc("Disable direct call optimization in "
|
|
|
|
"DSGraph construction"));
|
2003-02-08 20:18:39 +00:00
|
|
|
cl::opt<bool>
|
|
|
|
DisableFieldSensitivity("disable-ds-field-sensitivity", cl::Hidden,
|
|
|
|
cl::desc("Disable field sensitivity in DSGraphs"));
|
2003-02-05 21:59:58 +00:00
|
|
|
|
2002-10-01 22:33:50 +00:00
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
// 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.
|
|
|
|
///
|
2002-07-10 22:38:08 +00:00
|
|
|
class GraphBuilder : InstVisitor<GraphBuilder> {
|
|
|
|
DSGraph &G;
|
2003-09-20 21:48:16 +00:00
|
|
|
DSNodeHandle *RetNode; // Node that gets returned...
|
2004-01-28 09:15:42 +00:00
|
|
|
DSScalarMap &ScalarMap;
|
2005-01-30 23:51:02 +00:00
|
|
|
std::list<DSCallSite> *FunctionCalls;
|
2002-07-10 22:38:08 +00:00
|
|
|
|
|
|
|
public:
|
2005-04-21 21:13:18 +00:00
|
|
|
GraphBuilder(Function &f, DSGraph &g, DSNodeHandle &retNode,
|
2005-01-30 23:51:02 +00:00
|
|
|
std::list<DSCallSite> &fc)
|
2003-09-20 21:48:16 +00:00
|
|
|
: G(g), RetNode(&retNode), ScalarMap(G.getScalarMap()),
|
|
|
|
FunctionCalls(&fc) {
|
2002-07-18 00:12:30 +00:00
|
|
|
|
|
|
|
// Create scalar nodes for all pointer arguments...
|
2005-03-22 02:45:13 +00:00
|
|
|
for (Function::arg_iterator I = f.arg_begin(), E = f.arg_end();
|
|
|
|
I != E; ++I)
|
2002-10-01 22:33:50 +00:00
|
|
|
if (isPointerType(I->getType()))
|
|
|
|
getValueDest(*I);
|
2002-07-18 00:12:30 +00:00
|
|
|
|
2003-09-20 21:48:16 +00:00
|
|
|
visit(f); // Single pass over the function
|
2002-07-10 22:38:08 +00:00
|
|
|
}
|
|
|
|
|
2003-09-20 21:48:16 +00:00
|
|
|
// GraphBuilder ctor for working on the globals graph
|
|
|
|
GraphBuilder(DSGraph &g)
|
|
|
|
: G(g), RetNode(0), ScalarMap(G.getScalarMap()), FunctionCalls(0) {
|
|
|
|
}
|
|
|
|
|
|
|
|
void mergeInGlobalInitializer(GlobalVariable *GV);
|
|
|
|
|
2002-07-10 22:38:08 +00:00
|
|
|
private:
|
|
|
|
// Visitor functions, used to handle each instruction type we encounter...
|
|
|
|
friend class InstVisitor<GraphBuilder>;
|
2003-06-19 21:15:11 +00:00
|
|
|
void visitMallocInst(MallocInst &MI) { handleAlloc(MI, true); }
|
|
|
|
void visitAllocaInst(AllocaInst &AI) { handleAlloc(AI, false); }
|
|
|
|
void handleAlloc(AllocationInst &AI, bool isHeap);
|
2002-07-10 22:38:08 +00:00
|
|
|
|
|
|
|
void visitPHINode(PHINode &PN);
|
2005-02-25 01:27:48 +00:00
|
|
|
void visitSelectInst(SelectInst &SI);
|
2002-07-10 22:38:08 +00:00
|
|
|
|
2002-11-08 05:00:44 +00:00
|
|
|
void visitGetElementPtrInst(User &GEP);
|
2002-07-10 22:38:08 +00:00
|
|
|
void visitReturnInst(ReturnInst &RI);
|
|
|
|
void visitLoadInst(LoadInst &LI);
|
|
|
|
void visitStoreInst(StoreInst &SI);
|
|
|
|
void visitCallInst(CallInst &CI);
|
2003-09-20 16:34:13 +00:00
|
|
|
void visitInvokeInst(InvokeInst &II);
|
2005-03-05 19:04:31 +00:00
|
|
|
void visitSetCondInst(SetCondInst &SCI);
|
2002-12-06 21:17:10 +00:00
|
|
|
void visitFreeInst(FreeInst &FI);
|
2002-10-01 22:33:50 +00:00
|
|
|
void visitCastInst(CastInst &CI);
|
2003-02-04 00:59:50 +00:00
|
|
|
void visitInstruction(Instruction &I);
|
2002-07-10 22:38:08 +00:00
|
|
|
|
2003-09-20 16:34:13 +00:00
|
|
|
void visitCallSite(CallSite CS);
|
2004-03-04 20:33:47 +00:00
|
|
|
void visitVAArgInst(VAArgInst &I);
|
2003-09-20 21:48:16 +00:00
|
|
|
|
|
|
|
void MergeConstantInitIntoNode(DSNodeHandle &NH, Constant *C);
|
2002-07-10 22:38:08 +00:00
|
|
|
private:
|
|
|
|
// Helper functions used to implement the visitation functions...
|
|
|
|
|
2002-10-01 22:33:50 +00:00
|
|
|
/// createNode - Create a new DSNode, ensuring that it is properly added to
|
|
|
|
/// the graph.
|
|
|
|
///
|
2003-06-19 21:15:11 +00:00
|
|
|
DSNode *createNode(const Type *Ty = 0) {
|
|
|
|
DSNode *N = new DSNode(Ty, &G); // Create the node
|
2003-06-16 12:08:18 +00:00
|
|
|
if (DisableFieldSensitivity) {
|
2004-05-23 21:14:09 +00:00
|
|
|
// Create node handle referring to the old node so that it is
|
|
|
|
// immediately removed from the graph when the node handle is destroyed.
|
|
|
|
DSNodeHandle OldNNH = N;
|
2003-02-08 20:18:39 +00:00
|
|
|
N->foldNodeCompletely();
|
2003-06-16 12:08:18 +00:00
|
|
|
if (DSNode *FN = N->getForwardNode())
|
|
|
|
N = FN;
|
|
|
|
}
|
2002-11-02 00:13:20 +00:00
|
|
|
return N;
|
|
|
|
}
|
2002-10-01 22:33:50 +00:00
|
|
|
|
2002-11-03 21:27:48 +00:00
|
|
|
/// setDestTo - Set the ScalarMap entry for the specified value to point to
|
2002-11-02 00:13:20 +00:00
|
|
|
/// the specified destination. If the Value already points to a node, make
|
|
|
|
/// sure to merge the two destinations together.
|
2002-10-01 22:33:50 +00:00
|
|
|
///
|
2002-11-02 00:13:20 +00:00
|
|
|
void setDestTo(Value &V, const DSNodeHandle &NH);
|
2002-10-01 22:33:50 +00:00
|
|
|
|
2005-04-21 21:13:18 +00:00
|
|
|
/// getValueDest - Return the DSNode that the actual value points to.
|
2002-10-01 22:33:50 +00:00
|
|
|
///
|
2002-11-02 00:13:20 +00:00
|
|
|
DSNodeHandle getValueDest(Value &V);
|
2002-10-01 22:33:50 +00:00
|
|
|
|
|
|
|
/// 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
|
2002-10-31 06:52:26 +00:00
|
|
|
/// null), then we create a new node, link it, then return it.
|
2002-10-01 22:33:50 +00:00
|
|
|
///
|
2002-10-31 06:52:26 +00:00
|
|
|
DSNodeHandle &getLink(const DSNodeHandle &Node, unsigned Link = 0);
|
2002-07-10 22:38:08 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2003-11-11 22:41:34 +00:00
|
|
|
using namespace DS;
|
|
|
|
|
2002-07-10 22:38:08 +00:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// DSGraph constructor - Simply use the GraphBuilder to construct the local
|
|
|
|
// graph.
|
Create an equivalence class of global variables that DSA will never be able
to tell apart anyway, and only track the leader for of these equivalence
classes in our graphs.
This dramatically reduces the number of GlobalValue*'s that appear in scalar
maps, which A) reduces memory usage, by eliminating many many scalarmap entries
and B) reduces time for operations that need to execute an operation for each
global in the scalar map.
As an example, this reduces the memory used to analyze 176.gcc from 1GB to
511MB, which (while it's still way too much) is better because it doesn't hit
swap anymore. On eon, this shrinks the local graphs from 14MB to 6.8MB,
shrinks the bu+td graphs of povray from 50M to 40M, shrinks the TD graphs of
130.li from 8.8M to 3.6M, etc.
This change also speeds up DSA on large programs where this makes a big
difference. For example, 130.li goes from 1.17s -> 0.56s, 134.perl goes
from 2.14 -> 0.93s, povray goes from 15.63s->7.99s (!!!).
This also apparently either fixes the problem that caused DSA to crash on
perlbmk and gcc, or it hides it, because DSA now works on these. These
both take entirely too much time in the TD pass (147s for perl, 538s for
gcc, vs 7.67/5.9s in the bu pass for either one), but this is a known
problem that I'll deal with later.
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@20696 91177308-0d34-0410-b5e6-96231b3b80d8
2005-03-19 22:23:45 +00:00
|
|
|
DSGraph::DSGraph(EquivalenceClasses<GlobalValue*> &ECs, const TargetData &td,
|
|
|
|
Function &F, DSGraph *GG)
|
|
|
|
: GlobalsGraph(GG), ScalarMap(ECs), TD(td) {
|
2002-11-10 06:53:38 +00:00
|
|
|
PrintAuxCalls = false;
|
2003-07-02 04:37:26 +00:00
|
|
|
|
|
|
|
DEBUG(std::cerr << " [Loc] Calculating graph for: " << F.getName() << "\n");
|
|
|
|
|
2002-10-01 22:33:50 +00:00
|
|
|
// Use the graph builder to construct the local version of the graph
|
2003-09-20 21:48:16 +00:00
|
|
|
GraphBuilder B(F, *this, ReturnNodes[&F], FunctionCalls);
|
2002-11-18 21:44:19 +00:00
|
|
|
#ifndef NDEBUG
|
|
|
|
Timer::addPeakMemoryMeasurement();
|
|
|
|
#endif
|
2003-02-14 04:55:58 +00:00
|
|
|
|
2004-02-25 23:31:02 +00:00
|
|
|
// If there are any constant globals referenced in this function, merge their
|
|
|
|
// initializers into the local graph from the globals graph.
|
|
|
|
if (ScalarMap.global_begin() != ScalarMap.global_end()) {
|
|
|
|
ReachabilityCloner RC(*this, *GG, 0);
|
2005-04-21 21:13:18 +00:00
|
|
|
|
2004-02-25 23:31:02 +00:00
|
|
|
for (DSScalarMap::global_iterator I = ScalarMap.global_begin();
|
|
|
|
I != ScalarMap.global_end(); ++I)
|
|
|
|
if (GlobalVariable *GV = dyn_cast<GlobalVariable>(*I))
|
2004-03-03 23:00:19 +00:00
|
|
|
if (!GV->isExternal() && GV->isConstant())
|
2004-02-25 23:31:02 +00:00
|
|
|
RC.merge(ScalarMap[GV], GG->ScalarMap[GV]);
|
|
|
|
}
|
|
|
|
|
2003-01-23 22:05:33 +00:00
|
|
|
markIncompleteNodes(DSGraph::MarkFormalArgs);
|
2002-11-09 20:55:24 +00:00
|
|
|
|
|
|
|
// Remove any nodes made dead due to merging...
|
2003-01-23 22:05:33 +00:00
|
|
|
removeDeadNodes(DSGraph::KeepUnreachableGlobals);
|
2002-07-10 22:38:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Helper method implementations...
|
|
|
|
//
|
|
|
|
|
2002-11-02 00:13:20 +00:00
|
|
|
/// getValueDest - Return the DSNode that the actual value points to.
|
|
|
|
///
|
2002-11-08 05:00:44 +00:00
|
|
|
DSNodeHandle GraphBuilder::getValueDest(Value &Val) {
|
|
|
|
Value *V = &Val;
|
2004-11-03 18:51:26 +00:00
|
|
|
if (isa<Constant>(V) && cast<Constant>(V)->isNullValue())
|
2002-11-08 05:00:44 +00:00
|
|
|
return 0; // Null doesn't point to anything, don't add to ScalarMap!
|
2002-07-10 22:38:08 +00:00
|
|
|
|
2002-12-06 21:17:10 +00:00
|
|
|
DSNodeHandle &NH = ScalarMap[V];
|
2004-10-30 04:22:45 +00:00
|
|
|
if (!NH.isNull())
|
2002-12-06 21:17:10 +00:00
|
|
|
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.
|
2004-07-18 00:18:30 +00:00
|
|
|
DSNode* N;
|
|
|
|
if (GlobalValue* GV = dyn_cast<GlobalValue>(V)) {
|
2005-03-20 01:18:00 +00:00
|
|
|
// Create a new global node for this global variable.
|
2004-07-18 00:18:30 +00:00
|
|
|
N = createNode(GV->getType()->getElementType());
|
|
|
|
N->addGlobal(GV);
|
|
|
|
} else if (Constant *C = dyn_cast<Constant>(V)) {
|
|
|
|
if (ConstantExpr *CE = dyn_cast<ConstantExpr>(C)) {
|
2005-03-22 02:45:13 +00:00
|
|
|
if (CE->getOpcode() == Instruction::Cast) {
|
|
|
|
if (isa<PointerType>(CE->getOperand(0)->getType()))
|
|
|
|
NH = getValueDest(*CE->getOperand(0));
|
|
|
|
else
|
|
|
|
NH = createNode()->setUnknownNodeMarker();
|
|
|
|
} else if (CE->getOpcode() == Instruction::GetElementPtr) {
|
2002-11-08 05:00:44 +00:00
|
|
|
visitGetElementPtrInst(*CE);
|
2004-01-28 09:15:42 +00:00
|
|
|
DSScalarMap::iterator I = ScalarMap.find(CE);
|
2002-11-09 20:14:03 +00:00
|
|
|
assert(I != ScalarMap.end() && "GEP didn't get processed right?");
|
2003-02-09 23:04:12 +00:00
|
|
|
NH = I->second;
|
|
|
|
} else {
|
|
|
|
// This returns a conservative unknown node for any unhandled ConstExpr
|
2003-06-19 21:15:11 +00:00
|
|
|
return NH = createNode()->setUnknownNodeMarker();
|
2002-11-08 05:00:44 +00:00
|
|
|
}
|
2004-10-30 04:22:45 +00:00
|
|
|
if (NH.isNull()) { // (getelementptr null, X) returns null
|
2003-02-09 23:04:12 +00:00
|
|
|
ScalarMap.erase(V);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return NH;
|
2004-10-16 18:19:26 +00:00
|
|
|
} else if (isa<UndefValue>(C)) {
|
|
|
|
ScalarMap.erase(V);
|
|
|
|
return 0;
|
2002-11-08 05:00:44 +00:00
|
|
|
} else {
|
|
|
|
assert(0 && "Unknown constant type!");
|
|
|
|
}
|
2004-07-18 00:18:30 +00:00
|
|
|
N = createNode(); // just create a shadow node
|
2002-07-11 20:32:02 +00:00
|
|
|
} else {
|
2002-11-02 00:13:20 +00:00
|
|
|
// Otherwise just create a shadow node
|
2003-06-19 21:15:11 +00:00
|
|
|
N = createNode();
|
2002-07-10 22:38:08 +00:00
|
|
|
}
|
|
|
|
|
2004-07-07 06:12:52 +00:00
|
|
|
NH.setTo(N, 0); // Remember that we are pointing to it...
|
2002-11-02 00:13:20 +00:00
|
|
|
return NH;
|
2002-10-01 22:33:50 +00:00
|
|
|
}
|
2002-07-18 00:12:30 +00:00
|
|
|
|
2002-07-10 22:38:08 +00:00
|
|
|
|
2002-10-01 22:33:50 +00:00
|
|
|
/// 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.
|
|
|
|
///
|
2002-10-31 06:52:26 +00:00
|
|
|
DSNodeHandle &GraphBuilder::getLink(const DSNodeHandle &node, unsigned LinkNo) {
|
2002-10-01 22:33:50 +00:00
|
|
|
DSNodeHandle &Node = const_cast<DSNodeHandle&>(node);
|
2002-11-06 06:20:27 +00:00
|
|
|
DSNodeHandle &Link = Node.getLink(LinkNo);
|
2004-10-30 04:22:45 +00:00
|
|
|
if (Link.isNull()) {
|
2002-11-06 06:20:27 +00:00
|
|
|
// If the link hasn't been created yet, make and return a new shadow node
|
2003-06-19 21:15:11 +00:00
|
|
|
Link = createNode();
|
2002-11-06 06:20:27 +00:00
|
|
|
}
|
|
|
|
return Link;
|
2002-07-10 22:38:08 +00:00
|
|
|
}
|
|
|
|
|
2002-10-01 22:33:50 +00:00
|
|
|
|
2002-11-03 21:27:48 +00:00
|
|
|
/// setDestTo - Set the ScalarMap entry for the specified value to point to the
|
2002-11-02 00:13:20 +00:00
|
|
|
/// 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) {
|
2004-03-04 20:33:47 +00:00
|
|
|
ScalarMap[&V].mergeWith(NH);
|
2002-11-02 00:13:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-07-10 22:38:08 +00:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Specific instruction type handler implementations...
|
|
|
|
//
|
|
|
|
|
2002-10-01 22:33:50 +00:00
|
|
|
/// Alloca & Malloc instruction implementation - Simply create a new memory
|
|
|
|
/// object, pointing the scalar to it.
|
|
|
|
///
|
2003-06-19 21:15:11 +00:00
|
|
|
void GraphBuilder::handleAlloc(AllocationInst &AI, bool isHeap) {
|
|
|
|
DSNode *N = createNode();
|
|
|
|
if (isHeap)
|
|
|
|
N->setHeapNodeMarker();
|
|
|
|
else
|
|
|
|
N->setAllocaNodeMarker();
|
|
|
|
setDestTo(AI, N);
|
2002-07-10 22:38:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
2002-10-01 22:33:50 +00:00
|
|
|
if (!isPointerType(PN.getType())) return; // Only pointer PHIs
|
2002-07-10 22:38:08 +00:00
|
|
|
|
2002-11-03 21:27:48 +00:00
|
|
|
DSNodeHandle &PNDest = ScalarMap[&PN];
|
2002-07-10 22:38:08 +00:00
|
|
|
for (unsigned i = 0, e = PN.getNumIncomingValues(); i != e; ++i)
|
2002-11-02 00:13:20 +00:00
|
|
|
PNDest.mergeWith(getValueDest(*PN.getIncomingValue(i)));
|
2002-07-10 22:38:08 +00:00
|
|
|
}
|
|
|
|
|
2005-02-25 01:27:48 +00:00
|
|
|
void GraphBuilder::visitSelectInst(SelectInst &SI) {
|
|
|
|
if (!isPointerType(SI.getType())) return; // Only pointer Selects
|
2005-04-21 21:13:18 +00:00
|
|
|
|
2005-02-25 01:27:48 +00:00
|
|
|
DSNodeHandle &Dest = ScalarMap[&SI];
|
|
|
|
Dest.mergeWith(getValueDest(*SI.getOperand(1)));
|
|
|
|
Dest.mergeWith(getValueDest(*SI.getOperand(2)));
|
|
|
|
}
|
|
|
|
|
2005-03-05 19:04:31 +00:00
|
|
|
void GraphBuilder::visitSetCondInst(SetCondInst &SCI) {
|
|
|
|
if (!isPointerType(SCI.getOperand(0)->getType()) ||
|
|
|
|
isa<ConstantPointerNull>(SCI.getOperand(1))) return; // Only pointers
|
|
|
|
ScalarMap[SCI.getOperand(0)].mergeWith(getValueDest(*SCI.getOperand(1)));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-11-08 05:00:44 +00:00
|
|
|
void GraphBuilder::visitGetElementPtrInst(User &GEP) {
|
2002-10-01 22:33:50 +00:00
|
|
|
DSNodeHandle Value = getValueDest(*GEP.getOperand(0));
|
2005-02-24 19:55:31 +00:00
|
|
|
if (Value.isNull())
|
|
|
|
Value = createNode();
|
2002-10-01 22:33:50 +00:00
|
|
|
|
2003-11-14 17:09:46 +00:00
|
|
|
// As a special case, if all of the index operands of GEP are constant zeros,
|
|
|
|
// handle this just like we handle casts (ie, don't do much).
|
|
|
|
bool AllZeros = true;
|
|
|
|
for (unsigned i = 1, e = GEP.getNumOperands(); i != e; ++i)
|
|
|
|
if (GEP.getOperand(i) !=
|
|
|
|
Constant::getNullValue(GEP.getOperand(i)->getType())) {
|
|
|
|
AllZeros = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If all of the indices are zero, the result points to the operand without
|
|
|
|
// applying the type.
|
2005-03-18 23:18:20 +00:00
|
|
|
if (AllZeros || (!Value.isNull() &&
|
|
|
|
Value.getNode()->isNodeCompletelyFolded())) {
|
2003-11-14 17:09:46 +00:00
|
|
|
setDestTo(GEP, Value);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-10-31 05:45:02 +00:00
|
|
|
const PointerType *PTy = cast<PointerType>(GEP.getOperand(0)->getType());
|
|
|
|
const Type *CurTy = PTy->getElementType();
|
|
|
|
|
2002-11-06 06:20:27 +00:00
|
|
|
if (Value.getNode()->mergeTypeInfo(CurTy, Value.getOffset())) {
|
|
|
|
// If the node had to be folded... exit quickly
|
2002-11-02 00:13:20 +00:00
|
|
|
setDestTo(GEP, Value); // GEP result points to folded node
|
2002-10-31 05:45:02 +00:00
|
|
|
return;
|
|
|
|
}
|
2002-10-01 22:33:50 +00:00
|
|
|
|
2003-11-02 22:27:28 +00:00
|
|
|
const TargetData &TD = Value.getNode()->getTargetData();
|
|
|
|
|
2002-11-06 06:20:27 +00:00
|
|
|
#if 0
|
2002-10-31 05:45:02 +00:00
|
|
|
// Handle the pointer index specially...
|
|
|
|
if (GEP.getNumOperands() > 1 &&
|
2004-04-05 01:30:19 +00:00
|
|
|
(!isa<Constant>(GEP.getOperand(1)) ||
|
|
|
|
!cast<Constant>(GEP.getOperand(1))->isNullValue())) {
|
2002-10-31 05:45:02 +00:00
|
|
|
|
|
|
|
// 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();
|
2002-11-02 00:13:20 +00:00
|
|
|
setDestTo(GEP, Value); // GEP result points to folded node
|
2002-10-31 05:45:02 +00:00
|
|
|
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...
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2002-11-06 06:20:27 +00:00
|
|
|
#endif
|
2002-10-31 05:45:02 +00:00
|
|
|
|
|
|
|
// All of these subscripts are indexing INTO the elements we have...
|
2003-09-20 21:48:16 +00:00
|
|
|
unsigned Offset = 0;
|
2003-11-25 20:19:55 +00:00
|
|
|
for (gep_type_iterator I = gep_type_begin(GEP), E = gep_type_end(GEP);
|
|
|
|
I != E; ++I)
|
|
|
|
if (const StructType *STy = dyn_cast<StructType>(*I)) {
|
2005-01-12 04:51:37 +00:00
|
|
|
unsigned FieldNo =
|
|
|
|
(unsigned)cast<ConstantUInt>(I.getOperand())->getValue();
|
|
|
|
Offset += (unsigned)TD.getStructLayout(STy)->MemberOffsets[FieldNo];
|
2004-03-01 19:02:54 +00:00
|
|
|
} else if (const PointerType *PTy = dyn_cast<PointerType>(*I)) {
|
|
|
|
if (!isa<Constant>(I.getOperand()) ||
|
|
|
|
!cast<Constant>(I.getOperand())->isNullValue())
|
|
|
|
Value.getNode()->setArrayMarker();
|
2003-11-25 20:19:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-11-06 06:20:27 +00:00
|
|
|
#if 0
|
2003-11-25 20:19:55 +00:00
|
|
|
if (const SequentialType *STy = cast<SequentialType>(*I)) {
|
|
|
|
CurTy = STy->getElementType();
|
2002-10-02 06:24:36 +00:00
|
|
|
if (ConstantSInt *CS = dyn_cast<ConstantSInt>(GEP.getOperand(i))) {
|
2002-10-31 05:45:02 +00:00
|
|
|
Offset += CS->getValue()*TD.getTypeSize(CurTy);
|
2002-10-02 06:24:36 +00:00
|
|
|
} 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
|
2003-09-11 18:14:24 +00:00
|
|
|
// zeroth element.
|
2002-10-02 06:24:36 +00:00
|
|
|
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);
|
|
|
|
}
|
2002-10-01 22:33:50 +00:00
|
|
|
}
|
|
|
|
}
|
2003-11-25 20:19:55 +00:00
|
|
|
#endif
|
2002-10-01 22:33:50 +00:00
|
|
|
|
|
|
|
// Add in the offset calculated...
|
|
|
|
Value.setOffset(Value.getOffset()+Offset);
|
|
|
|
|
2005-12-06 18:04:30 +00:00
|
|
|
// Check the offset
|
|
|
|
DSNode *N = Value.getNode();
|
|
|
|
if (N &&
|
|
|
|
!N->isNodeCompletelyFolded() &&
|
|
|
|
(N->getSize() != 0 || Offset != 0) &&
|
|
|
|
!N->isForwarding()) {
|
|
|
|
if ((Offset >= N->getSize()) || int(Offset) < 0) {
|
|
|
|
// Accessing offsets out of node size range
|
|
|
|
// This is seen in the "magic" struct in named (from bind), where the
|
|
|
|
// fourth field is an array of length 0, presumably used to create struct
|
|
|
|
// instances of different sizes
|
|
|
|
|
|
|
|
// Collapse the node since its size is now variable
|
|
|
|
N->foldNodeCompletely();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Value is now the pointer we want to GEP to be...
|
2002-11-02 00:13:20 +00:00
|
|
|
setDestTo(GEP, Value);
|
2002-07-10 22:38:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void GraphBuilder::visitLoadInst(LoadInst &LI) {
|
2002-11-02 00:13:20 +00:00
|
|
|
DSNodeHandle Ptr = getValueDest(*LI.getOperand(0));
|
2005-02-25 01:27:48 +00:00
|
|
|
if (Ptr.isNull())
|
|
|
|
Ptr = createNode();
|
2002-11-02 00:13:20 +00:00
|
|
|
|
|
|
|
// Make that the node is read from...
|
2003-06-19 21:15:11 +00:00
|
|
|
Ptr.getNode()->setReadMarker();
|
2002-10-31 05:45:02 +00:00
|
|
|
|
|
|
|
// Ensure a typerecord exists...
|
2003-03-03 17:13:31 +00:00
|
|
|
Ptr.getNode()->mergeTypeInfo(LI.getType(), Ptr.getOffset(), false);
|
2002-11-02 00:13:20 +00:00
|
|
|
|
2002-10-01 22:33:50 +00:00
|
|
|
if (isPointerType(LI.getType()))
|
2002-11-02 00:13:20 +00:00
|
|
|
setDestTo(LI, getLink(Ptr));
|
2002-07-10 22:38:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void GraphBuilder::visitStoreInst(StoreInst &SI) {
|
2002-10-31 05:45:02 +00:00
|
|
|
const Type *StoredTy = SI.getOperand(0)->getType();
|
2002-11-02 00:13:20 +00:00
|
|
|
DSNodeHandle Dest = getValueDest(*SI.getOperand(1));
|
2004-03-04 20:33:47 +00:00
|
|
|
if (Dest.isNull()) return;
|
2002-11-02 00:13:20 +00:00
|
|
|
|
2002-12-06 21:17:10 +00:00
|
|
|
// Mark that the node is written to...
|
2003-06-19 21:15:11 +00:00
|
|
|
Dest.getNode()->setModifiedMarker();
|
2002-10-31 05:45:02 +00:00
|
|
|
|
2003-09-20 21:48:16 +00:00
|
|
|
// Ensure a type-record exists...
|
2002-11-06 06:20:27 +00:00
|
|
|
Dest.getNode()->mergeTypeInfo(StoredTy, Dest.getOffset());
|
2002-10-01 22:33:50 +00:00
|
|
|
|
|
|
|
// Avoid adding edges from null, or processing non-"pointer" stores
|
2002-11-02 00:13:20 +00:00
|
|
|
if (isPointerType(StoredTy))
|
2002-10-01 22:33:50 +00:00
|
|
|
Dest.addEdgeTo(getValueDest(*SI.getOperand(0)));
|
2002-07-10 22:38:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void GraphBuilder::visitReturnInst(ReturnInst &RI) {
|
2002-11-02 00:13:20 +00:00
|
|
|
if (RI.getNumOperands() && isPointerType(RI.getOperand(0)->getType()))
|
2003-09-20 21:48:16 +00:00
|
|
|
RetNode->mergeWith(getValueDest(*RI.getOperand(0)));
|
2002-07-10 22:38:08 +00:00
|
|
|
}
|
|
|
|
|
2004-03-04 20:33:47 +00:00
|
|
|
void GraphBuilder::visitVAArgInst(VAArgInst &I) {
|
2005-06-18 18:34:52 +00:00
|
|
|
//FIXME: also updates the argument
|
2004-03-04 20:33:47 +00:00
|
|
|
DSNodeHandle Ptr = getValueDest(*I.getOperand(0));
|
|
|
|
if (Ptr.isNull()) return;
|
|
|
|
|
|
|
|
// Make that the node is read from.
|
|
|
|
Ptr.getNode()->setReadMarker();
|
|
|
|
|
2004-10-30 04:22:45 +00:00
|
|
|
// Ensure a type record exists.
|
|
|
|
DSNode *PtrN = Ptr.getNode();
|
|
|
|
PtrN->mergeTypeInfo(I.getType(), Ptr.getOffset(), false);
|
2004-03-04 20:33:47 +00:00
|
|
|
|
|
|
|
if (isPointerType(I.getType()))
|
|
|
|
setDestTo(I, getLink(Ptr));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-07-10 22:38:08 +00:00
|
|
|
void GraphBuilder::visitCallInst(CallInst &CI) {
|
2003-09-20 16:34:13 +00:00
|
|
|
visitCallSite(&CI);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GraphBuilder::visitInvokeInst(InvokeInst &II) {
|
|
|
|
visitCallSite(&II);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GraphBuilder::visitCallSite(CallSite CS) {
|
2004-02-26 22:07:22 +00:00
|
|
|
Value *Callee = CS.getCalledValue();
|
|
|
|
|
2003-09-20 16:50:46 +00:00
|
|
|
// Special case handling of certain libc allocation functions here.
|
2004-02-26 22:07:22 +00:00
|
|
|
if (Function *F = dyn_cast<Function>(Callee))
|
2003-09-20 16:50:46 +00:00
|
|
|
if (F->isExternal())
|
2004-02-13 16:09:54 +00:00
|
|
|
switch (F->getIntrinsicID()) {
|
2004-03-13 00:24:00 +00:00
|
|
|
case Intrinsic::vastart:
|
2004-03-04 20:33:47 +00:00
|
|
|
getValueDest(*CS.getInstruction()).getNode()->setAllocaNodeMarker();
|
|
|
|
return;
|
2004-03-13 00:24:00 +00:00
|
|
|
case Intrinsic::vacopy:
|
2004-03-04 20:33:47 +00:00
|
|
|
getValueDest(*CS.getInstruction()).
|
|
|
|
mergeWith(getValueDest(**(CS.arg_begin())));
|
|
|
|
return;
|
2004-03-13 00:24:00 +00:00
|
|
|
case Intrinsic::vaend:
|
2004-03-04 20:33:47 +00:00
|
|
|
return; // noop
|
2004-02-13 16:09:54 +00:00
|
|
|
case Intrinsic::memmove:
|
|
|
|
case Intrinsic::memcpy: {
|
|
|
|
// Merge the first & second arguments, and mark the memory read and
|
2003-11-08 21:55:50 +00:00
|
|
|
// modified.
|
2004-02-13 16:09:54 +00:00
|
|
|
DSNodeHandle RetNH = getValueDest(**CS.arg_begin());
|
2003-11-09 03:32:52 +00:00
|
|
|
RetNH.mergeWith(getValueDest(**(CS.arg_begin()+1)));
|
|
|
|
if (DSNode *N = RetNH.getNode())
|
|
|
|
N->setModifiedMarker()->setReadMarker();
|
|
|
|
return;
|
2004-02-13 16:09:54 +00:00
|
|
|
}
|
2004-02-16 18:37:40 +00:00
|
|
|
case Intrinsic::memset:
|
|
|
|
// Mark the memory modified.
|
|
|
|
if (DSNode *N = getValueDest(**CS.arg_begin()).getNode())
|
|
|
|
N->setModifiedMarker();
|
|
|
|
return;
|
2004-02-13 16:09:54 +00:00
|
|
|
default:
|
2005-12-19 17:38:39 +00:00
|
|
|
// Determine if the called function is one of the specified heap
|
|
|
|
// allocation functions
|
|
|
|
for (cl::list<std::string>::iterator AllocFunc = AllocList.begin(),
|
|
|
|
LastAllocFunc = AllocList.end();
|
|
|
|
AllocFunc != LastAllocFunc;
|
|
|
|
++AllocFunc) {
|
|
|
|
if (F->getName() == *(AllocFunc)) {
|
|
|
|
setDestTo(*CS.getInstruction(),
|
|
|
|
createNode()->setHeapNodeMarker()->setModifiedMarker());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-12-19 19:54:23 +00:00
|
|
|
// Determine if the called function is one of the specified heap
|
|
|
|
// free functions
|
|
|
|
for (cl::list<std::string>::iterator FreeFunc = FreeList.begin(),
|
|
|
|
LastFreeFunc = FreeList.end();
|
|
|
|
FreeFunc != LastFreeFunc;
|
|
|
|
++FreeFunc) {
|
|
|
|
if (F->getName() == *(FreeFunc)) {
|
|
|
|
// Mark that the node is written to...
|
|
|
|
if (DSNode *N = getValueDest(*(CS.getArgument(0))).getNode())
|
|
|
|
N->setModifiedMarker()->setHeapNodeMarker();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-05-25 08:14:52 +00:00
|
|
|
if (F->getName() == "calloc" || F->getName() == "posix_memalign" ||
|
|
|
|
F->getName() == "memalign" || F->getName() == "valloc") {
|
2004-02-13 16:09:54 +00:00
|
|
|
setDestTo(*CS.getInstruction(),
|
|
|
|
createNode()->setHeapNodeMarker()->setModifiedMarker());
|
|
|
|
return;
|
|
|
|
} else if (F->getName() == "realloc") {
|
|
|
|
DSNodeHandle RetNH = getValueDest(*CS.getInstruction());
|
2004-11-03 18:51:26 +00:00
|
|
|
if (CS.arg_begin() != CS.arg_end())
|
|
|
|
RetNH.mergeWith(getValueDest(**CS.arg_begin()));
|
2004-02-13 16:09:54 +00:00
|
|
|
if (DSNode *N = RetNH.getNode())
|
|
|
|
N->setHeapNodeMarker()->setModifiedMarker()->setReadMarker();
|
|
|
|
return;
|
2004-02-26 03:43:08 +00:00
|
|
|
} else if (F->getName() == "memmove") {
|
|
|
|
// Merge the first & second arguments, and mark the memory read and
|
|
|
|
// modified.
|
|
|
|
DSNodeHandle RetNH = getValueDest(**CS.arg_begin());
|
|
|
|
RetNH.mergeWith(getValueDest(**(CS.arg_begin()+1)));
|
|
|
|
if (DSNode *N = RetNH.getNode())
|
|
|
|
N->setModifiedMarker()->setReadMarker();
|
|
|
|
return;
|
|
|
|
|
2004-02-24 22:02:48 +00:00
|
|
|
} else if (F->getName() == "atoi" || F->getName() == "atof" ||
|
2004-02-25 17:43:20 +00:00
|
|
|
F->getName() == "atol" || F->getName() == "atoll" ||
|
2004-02-24 22:17:00 +00:00
|
|
|
F->getName() == "remove" || F->getName() == "unlink" ||
|
2004-02-25 17:43:20 +00:00
|
|
|
F->getName() == "rename" || F->getName() == "memcmp" ||
|
|
|
|
F->getName() == "strcmp" || F->getName() == "strncmp" ||
|
|
|
|
F->getName() == "execl" || F->getName() == "execlp" ||
|
|
|
|
F->getName() == "execle" || F->getName() == "execv" ||
|
2004-02-25 23:06:40 +00:00
|
|
|
F->getName() == "execvp" || F->getName() == "chmod" ||
|
|
|
|
F->getName() == "puts" || F->getName() == "write" ||
|
|
|
|
F->getName() == "open" || F->getName() == "create" ||
|
|
|
|
F->getName() == "truncate" || F->getName() == "chdir" ||
|
|
|
|
F->getName() == "mkdir" || F->getName() == "rmdir") {
|
2004-02-24 22:17:00 +00:00
|
|
|
// These functions read all of their pointer operands.
|
|
|
|
for (CallSite::arg_iterator AI = CS.arg_begin(), E = CS.arg_end();
|
|
|
|
AI != E; ++AI) {
|
|
|
|
if (isPointerType((*AI)->getType()))
|
|
|
|
if (DSNode *N = getValueDest(**AI).getNode())
|
2005-04-21 21:13:18 +00:00
|
|
|
N->setReadMarker();
|
2004-02-24 22:17:00 +00:00
|
|
|
}
|
2004-02-16 22:57:19 +00:00
|
|
|
return;
|
2004-02-25 23:06:40 +00:00
|
|
|
} else if (F->getName() == "read" || F->getName() == "pipe" ||
|
2004-02-27 20:04:48 +00:00
|
|
|
F->getName() == "wait" || F->getName() == "time") {
|
2004-02-25 23:06:40 +00:00
|
|
|
// These functions write all of their pointer operands.
|
|
|
|
for (CallSite::arg_iterator AI = CS.arg_begin(), E = CS.arg_end();
|
|
|
|
AI != E; ++AI) {
|
|
|
|
if (isPointerType((*AI)->getType()))
|
|
|
|
if (DSNode *N = getValueDest(**AI).getNode())
|
2005-04-21 21:13:18 +00:00
|
|
|
N->setModifiedMarker();
|
2004-02-25 23:06:40 +00:00
|
|
|
}
|
|
|
|
return;
|
2004-02-25 17:43:20 +00:00
|
|
|
} else if (F->getName() == "stat" || F->getName() == "fstat" ||
|
|
|
|
F->getName() == "lstat") {
|
|
|
|
// These functions read their first operand if its a pointer.
|
|
|
|
CallSite::arg_iterator AI = CS.arg_begin();
|
|
|
|
if (isPointerType((*AI)->getType())) {
|
|
|
|
DSNodeHandle Path = getValueDest(**AI);
|
|
|
|
if (DSNode *N = Path.getNode()) N->setReadMarker();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Then they write into the stat buffer.
|
|
|
|
DSNodeHandle StatBuf = getValueDest(**++AI);
|
|
|
|
if (DSNode *N = StatBuf.getNode()) {
|
|
|
|
N->setModifiedMarker();
|
|
|
|
const Type *StatTy = F->getFunctionType()->getParamType(1);
|
|
|
|
if (const PointerType *PTy = dyn_cast<PointerType>(StatTy))
|
|
|
|
N->mergeTypeInfo(PTy->getElementType(), StatBuf.getOffset());
|
|
|
|
}
|
2004-03-04 21:03:54 +00:00
|
|
|
return;
|
|
|
|
} else if (F->getName() == "strtod" || F->getName() == "strtof" ||
|
|
|
|
F->getName() == "strtold") {
|
|
|
|
// These functions read the first pointer
|
|
|
|
if (DSNode *Str = getValueDest(**CS.arg_begin()).getNode()) {
|
|
|
|
Str->setReadMarker();
|
|
|
|
// If the second parameter is passed, it will point to the first
|
|
|
|
// argument node.
|
|
|
|
const DSNodeHandle &EndPtrNH = getValueDest(**(CS.arg_begin()+1));
|
|
|
|
if (DSNode *End = EndPtrNH.getNode()) {
|
|
|
|
End->mergeTypeInfo(PointerType::get(Type::SByteTy),
|
|
|
|
EndPtrNH.getOffset(), false);
|
|
|
|
End->setModifiedMarker();
|
|
|
|
DSNodeHandle &Link = getLink(EndPtrNH);
|
|
|
|
Link.mergeWith(getValueDest(**CS.arg_begin()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-02-25 17:43:20 +00:00
|
|
|
return;
|
2004-02-27 20:04:48 +00:00
|
|
|
} else if (F->getName() == "fopen" || F->getName() == "fdopen" ||
|
|
|
|
F->getName() == "freopen") {
|
|
|
|
// These functions read all of their pointer operands.
|
|
|
|
for (CallSite::arg_iterator AI = CS.arg_begin(), E = CS.arg_end();
|
|
|
|
AI != E; ++AI)
|
|
|
|
if (isPointerType((*AI)->getType()))
|
|
|
|
if (DSNode *N = getValueDest(**AI).getNode())
|
|
|
|
N->setReadMarker();
|
2005-04-21 21:13:18 +00:00
|
|
|
|
2004-02-13 20:05:32 +00:00
|
|
|
// fopen allocates in an unknown way and writes to the file
|
|
|
|
// descriptor. Also, merge the allocated type into the node.
|
|
|
|
DSNodeHandle Result = getValueDest(*CS.getInstruction());
|
2004-02-24 22:02:48 +00:00
|
|
|
if (DSNode *N = Result.getNode()) {
|
|
|
|
N->setModifiedMarker()->setUnknownNodeMarker();
|
|
|
|
const Type *RetTy = F->getFunctionType()->getReturnType();
|
|
|
|
if (const PointerType *PTy = dyn_cast<PointerType>(RetTy))
|
|
|
|
N->mergeTypeInfo(PTy->getElementType(), Result.getOffset());
|
|
|
|
}
|
2004-02-27 20:04:48 +00:00
|
|
|
|
|
|
|
// If this is freopen, merge the file descriptor passed in with the
|
|
|
|
// result.
|
2004-12-08 16:22:26 +00:00
|
|
|
if (F->getName() == "freopen") {
|
|
|
|
// ICC doesn't handle getting the iterator, decrementing and
|
|
|
|
// dereferencing it in one operation without error. Do it in 2 steps
|
|
|
|
CallSite::arg_iterator compit = CS.arg_end();
|
|
|
|
Result.mergeWith(getValueDest(**--compit));
|
|
|
|
}
|
2004-02-24 22:02:48 +00:00
|
|
|
return;
|
2004-02-13 20:05:32 +00:00
|
|
|
} else if (F->getName() == "fclose" && CS.arg_end()-CS.arg_begin() ==1){
|
|
|
|
// fclose reads and deallocates the memory in an unknown way for the
|
|
|
|
// file descriptor. It merges the FILE type into the descriptor.
|
|
|
|
DSNodeHandle H = getValueDest(**CS.arg_begin());
|
2004-02-24 22:02:48 +00:00
|
|
|
if (DSNode *N = H.getNode()) {
|
|
|
|
N->setReadMarker()->setUnknownNodeMarker();
|
|
|
|
const Type *ArgTy = F->getFunctionType()->getParamType(0);
|
|
|
|
if (const PointerType *PTy = dyn_cast<PointerType>(ArgTy))
|
|
|
|
N->mergeTypeInfo(PTy->getElementType(), H.getOffset());
|
|
|
|
}
|
2004-02-13 20:05:32 +00:00
|
|
|
return;
|
2005-04-21 21:13:18 +00:00
|
|
|
} else if (CS.arg_end()-CS.arg_begin() == 1 &&
|
2004-02-16 22:57:19 +00:00
|
|
|
(F->getName() == "fflush" || F->getName() == "feof" ||
|
|
|
|
F->getName() == "fileno" || F->getName() == "clearerr" ||
|
2004-02-25 23:06:40 +00:00
|
|
|
F->getName() == "rewind" || F->getName() == "ftell" ||
|
2004-02-27 20:04:48 +00:00
|
|
|
F->getName() == "ferror" || F->getName() == "fgetc" ||
|
|
|
|
F->getName() == "fgetc" || F->getName() == "_IO_getc")) {
|
2004-02-16 22:57:19 +00:00
|
|
|
// fflush reads and writes the memory for the file descriptor. It
|
2004-02-13 21:21:48 +00:00
|
|
|
// merges the FILE type into the descriptor.
|
|
|
|
DSNodeHandle H = getValueDest(**CS.arg_begin());
|
2004-02-24 22:02:48 +00:00
|
|
|
if (DSNode *N = H.getNode()) {
|
|
|
|
N->setReadMarker()->setModifiedMarker();
|
2005-04-21 21:13:18 +00:00
|
|
|
|
2004-02-24 22:02:48 +00:00
|
|
|
const Type *ArgTy = F->getFunctionType()->getParamType(0);
|
|
|
|
if (const PointerType *PTy = dyn_cast<PointerType>(ArgTy))
|
|
|
|
N->mergeTypeInfo(PTy->getElementType(), H.getOffset());
|
|
|
|
}
|
|
|
|
return;
|
2005-04-21 21:13:18 +00:00
|
|
|
} else if (CS.arg_end()-CS.arg_begin() == 4 &&
|
2004-02-24 22:02:48 +00:00
|
|
|
(F->getName() == "fwrite" || F->getName() == "fread")) {
|
|
|
|
// fread writes the first operand, fwrite reads it. They both
|
|
|
|
// read/write the FILE descriptor, and merges the FILE type.
|
2004-12-08 16:22:26 +00:00
|
|
|
CallSite::arg_iterator compit = CS.arg_end();
|
|
|
|
DSNodeHandle H = getValueDest(**--compit);
|
2004-02-24 22:02:48 +00:00
|
|
|
if (DSNode *N = H.getNode()) {
|
|
|
|
N->setReadMarker()->setModifiedMarker();
|
|
|
|
const Type *ArgTy = F->getFunctionType()->getParamType(3);
|
|
|
|
if (const PointerType *PTy = dyn_cast<PointerType>(ArgTy))
|
|
|
|
N->mergeTypeInfo(PTy->getElementType(), H.getOffset());
|
|
|
|
}
|
|
|
|
|
|
|
|
H = getValueDest(**CS.arg_begin());
|
|
|
|
if (DSNode *N = H.getNode())
|
|
|
|
if (F->getName() == "fwrite")
|
|
|
|
N->setReadMarker();
|
|
|
|
else
|
|
|
|
N->setModifiedMarker();
|
2004-02-13 21:21:48 +00:00
|
|
|
return;
|
|
|
|
} else if (F->getName() == "fgets" && CS.arg_end()-CS.arg_begin() == 3){
|
2004-02-16 22:57:19 +00:00
|
|
|
// fgets reads and writes the memory for the file descriptor. It
|
2004-02-13 21:21:48 +00:00
|
|
|
// merges the FILE type into the descriptor, and writes to the
|
|
|
|
// argument. It returns the argument as well.
|
|
|
|
CallSite::arg_iterator AI = CS.arg_begin();
|
|
|
|
DSNodeHandle H = getValueDest(**AI);
|
|
|
|
if (DSNode *N = H.getNode())
|
|
|
|
N->setModifiedMarker(); // Writes buffer
|
|
|
|
H.mergeWith(getValueDest(*CS.getInstruction())); // Returns buffer
|
|
|
|
++AI; ++AI;
|
|
|
|
|
|
|
|
// Reads and writes file descriptor, merge in FILE type.
|
2004-02-25 17:43:20 +00:00
|
|
|
H = getValueDest(**AI);
|
|
|
|
if (DSNode *N = H.getNode()) {
|
|
|
|
N->setReadMarker()->setModifiedMarker();
|
|
|
|
const Type *ArgTy = F->getFunctionType()->getParamType(2);
|
|
|
|
if (const PointerType *PTy = dyn_cast<PointerType>(ArgTy))
|
|
|
|
N->mergeTypeInfo(PTy->getElementType(), H.getOffset());
|
|
|
|
}
|
|
|
|
return;
|
2004-02-25 23:06:40 +00:00
|
|
|
} else if (F->getName() == "ungetc" || F->getName() == "fputc" ||
|
|
|
|
F->getName() == "fputs" || F->getName() == "putc" ||
|
2004-02-27 20:04:48 +00:00
|
|
|
F->getName() == "ftell" || F->getName() == "rewind" ||
|
|
|
|
F->getName() == "_IO_putc") {
|
|
|
|
// These functions read and write the memory for the file descriptor,
|
|
|
|
// which is passes as the last argument.
|
2004-12-08 16:22:26 +00:00
|
|
|
CallSite::arg_iterator compit = CS.arg_end();
|
|
|
|
DSNodeHandle H = getValueDest(**--compit);
|
2004-02-24 22:02:48 +00:00
|
|
|
if (DSNode *N = H.getNode()) {
|
2004-02-13 21:21:48 +00:00
|
|
|
N->setReadMarker()->setModifiedMarker();
|
2004-12-08 16:22:26 +00:00
|
|
|
FunctionType::param_iterator compit2 = F->getFunctionType()->param_end();
|
|
|
|
const Type *ArgTy = *--compit2;
|
2004-02-24 22:02:48 +00:00
|
|
|
if (const PointerType *PTy = dyn_cast<PointerType>(ArgTy))
|
|
|
|
N->mergeTypeInfo(PTy->getElementType(), H.getOffset());
|
|
|
|
}
|
2004-02-25 23:06:40 +00:00
|
|
|
|
|
|
|
// Any pointer arguments are read.
|
|
|
|
for (CallSite::arg_iterator AI = CS.arg_begin(), E = CS.arg_end();
|
|
|
|
AI != E; ++AI)
|
|
|
|
if (isPointerType((*AI)->getType()))
|
|
|
|
if (DSNode *N = getValueDest(**AI).getNode())
|
2005-04-21 21:13:18 +00:00
|
|
|
N->setReadMarker();
|
2004-02-25 23:06:40 +00:00
|
|
|
return;
|
|
|
|
} else if (F->getName() == "fseek" || F->getName() == "fgetpos" ||
|
|
|
|
F->getName() == "fsetpos") {
|
|
|
|
// These functions read and write the memory for the file descriptor,
|
|
|
|
// and read/write all other arguments.
|
|
|
|
DSNodeHandle H = getValueDest(**CS.arg_begin());
|
|
|
|
if (DSNode *N = H.getNode()) {
|
2004-12-08 16:22:26 +00:00
|
|
|
FunctionType::param_iterator compit2 = F->getFunctionType()->param_end();
|
|
|
|
const Type *ArgTy = *--compit2;
|
2004-02-25 23:06:40 +00:00
|
|
|
if (const PointerType *PTy = dyn_cast<PointerType>(ArgTy))
|
|
|
|
N->mergeTypeInfo(PTy->getElementType(), H.getOffset());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Any pointer arguments are read.
|
|
|
|
for (CallSite::arg_iterator AI = CS.arg_begin(), E = CS.arg_end();
|
|
|
|
AI != E; ++AI)
|
|
|
|
if (isPointerType((*AI)->getType()))
|
|
|
|
if (DSNode *N = getValueDest(**AI).getNode())
|
|
|
|
N->setReadMarker()->setModifiedMarker();
|
2004-02-13 21:21:48 +00:00
|
|
|
return;
|
2004-02-20 20:27:11 +00:00
|
|
|
} else if (F->getName() == "printf" || F->getName() == "fprintf" ||
|
|
|
|
F->getName() == "sprintf") {
|
2004-02-13 21:21:48 +00:00
|
|
|
CallSite::arg_iterator AI = CS.arg_begin(), E = CS.arg_end();
|
|
|
|
|
|
|
|
if (F->getName() == "fprintf") {
|
|
|
|
// fprintf reads and writes the FILE argument, and applies the type
|
|
|
|
// to it.
|
|
|
|
DSNodeHandle H = getValueDest(**AI);
|
|
|
|
if (DSNode *N = H.getNode()) {
|
|
|
|
N->setModifiedMarker();
|
|
|
|
const Type *ArgTy = (*AI)->getType();
|
|
|
|
if (const PointerType *PTy = dyn_cast<PointerType>(ArgTy))
|
|
|
|
N->mergeTypeInfo(PTy->getElementType(), H.getOffset());
|
|
|
|
}
|
2004-02-20 20:27:11 +00:00
|
|
|
} else if (F->getName() == "sprintf") {
|
|
|
|
// sprintf writes the first string argument.
|
|
|
|
DSNodeHandle H = getValueDest(**AI++);
|
|
|
|
if (DSNode *N = H.getNode()) {
|
|
|
|
N->setModifiedMarker();
|
|
|
|
const Type *ArgTy = (*AI)->getType();
|
|
|
|
if (const PointerType *PTy = dyn_cast<PointerType>(ArgTy))
|
|
|
|
N->mergeTypeInfo(PTy->getElementType(), H.getOffset());
|
|
|
|
}
|
2004-02-13 21:21:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (; AI != E; ++AI) {
|
|
|
|
// printf reads all pointer arguments.
|
|
|
|
if (isPointerType((*AI)->getType()))
|
|
|
|
if (DSNode *N = getValueDest(**AI).getNode())
|
2005-04-21 21:13:18 +00:00
|
|
|
N->setReadMarker();
|
2004-02-13 21:21:48 +00:00
|
|
|
}
|
2004-03-04 21:03:54 +00:00
|
|
|
return;
|
|
|
|
} else if (F->getName() == "vprintf" || F->getName() == "vfprintf" ||
|
|
|
|
F->getName() == "vsprintf") {
|
|
|
|
CallSite::arg_iterator AI = CS.arg_begin(), E = CS.arg_end();
|
|
|
|
|
|
|
|
if (F->getName() == "vfprintf") {
|
|
|
|
// ffprintf reads and writes the FILE argument, and applies the type
|
|
|
|
// to it.
|
|
|
|
DSNodeHandle H = getValueDest(**AI);
|
|
|
|
if (DSNode *N = H.getNode()) {
|
|
|
|
N->setModifiedMarker()->setReadMarker();
|
|
|
|
const Type *ArgTy = (*AI)->getType();
|
|
|
|
if (const PointerType *PTy = dyn_cast<PointerType>(ArgTy))
|
|
|
|
N->mergeTypeInfo(PTy->getElementType(), H.getOffset());
|
|
|
|
}
|
|
|
|
++AI;
|
|
|
|
} else if (F->getName() == "vsprintf") {
|
|
|
|
// vsprintf writes the first string argument.
|
|
|
|
DSNodeHandle H = getValueDest(**AI++);
|
|
|
|
if (DSNode *N = H.getNode()) {
|
|
|
|
N->setModifiedMarker();
|
|
|
|
const Type *ArgTy = (*AI)->getType();
|
|
|
|
if (const PointerType *PTy = dyn_cast<PointerType>(ArgTy))
|
|
|
|
N->mergeTypeInfo(PTy->getElementType(), H.getOffset());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read the format
|
|
|
|
if (AI != E) {
|
|
|
|
if (isPointerType((*AI)->getType()))
|
|
|
|
if (DSNode *N = getValueDest(**AI).getNode())
|
|
|
|
N->setReadMarker();
|
|
|
|
++AI;
|
|
|
|
}
|
2005-04-21 21:13:18 +00:00
|
|
|
|
2004-03-04 21:03:54 +00:00
|
|
|
// Read the valist, and the pointed-to objects.
|
|
|
|
if (AI != E && isPointerType((*AI)->getType())) {
|
|
|
|
const DSNodeHandle &VAList = getValueDest(**AI);
|
|
|
|
if (DSNode *N = VAList.getNode()) {
|
|
|
|
N->setReadMarker();
|
|
|
|
N->mergeTypeInfo(PointerType::get(Type::SByteTy),
|
|
|
|
VAList.getOffset(), false);
|
|
|
|
|
|
|
|
DSNodeHandle &VAListObjs = getLink(VAList);
|
|
|
|
VAListObjs.getNode()->setReadMarker();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-02-20 23:27:09 +00:00
|
|
|
return;
|
2004-02-20 20:27:11 +00:00
|
|
|
} else if (F->getName() == "scanf" || F->getName() == "fscanf" ||
|
|
|
|
F->getName() == "sscanf") {
|
|
|
|
CallSite::arg_iterator AI = CS.arg_begin(), E = CS.arg_end();
|
|
|
|
|
|
|
|
if (F->getName() == "fscanf") {
|
|
|
|
// fscanf reads and writes the FILE argument, and applies the type
|
|
|
|
// to it.
|
|
|
|
DSNodeHandle H = getValueDest(**AI);
|
|
|
|
if (DSNode *N = H.getNode()) {
|
|
|
|
N->setReadMarker();
|
|
|
|
const Type *ArgTy = (*AI)->getType();
|
|
|
|
if (const PointerType *PTy = dyn_cast<PointerType>(ArgTy))
|
|
|
|
N->mergeTypeInfo(PTy->getElementType(), H.getOffset());
|
|
|
|
}
|
|
|
|
} else if (F->getName() == "sscanf") {
|
|
|
|
// sscanf reads the first string argument.
|
|
|
|
DSNodeHandle H = getValueDest(**AI++);
|
|
|
|
if (DSNode *N = H.getNode()) {
|
|
|
|
N->setReadMarker();
|
|
|
|
const Type *ArgTy = (*AI)->getType();
|
|
|
|
if (const PointerType *PTy = dyn_cast<PointerType>(ArgTy))
|
|
|
|
N->mergeTypeInfo(PTy->getElementType(), H.getOffset());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (; AI != E; ++AI) {
|
|
|
|
// scanf writes all pointer arguments.
|
|
|
|
if (isPointerType((*AI)->getType()))
|
|
|
|
if (DSNode *N = getValueDest(**AI).getNode())
|
2005-04-21 21:13:18 +00:00
|
|
|
N->setModifiedMarker();
|
2004-02-20 20:27:11 +00:00
|
|
|
}
|
2004-02-20 23:27:09 +00:00
|
|
|
return;
|
2004-02-20 20:27:11 +00:00
|
|
|
} else if (F->getName() == "strtok") {
|
|
|
|
// strtok reads and writes the first argument, returning it. It reads
|
|
|
|
// its second arg. FIXME: strtok also modifies some hidden static
|
|
|
|
// data. Someday this might matter.
|
|
|
|
CallSite::arg_iterator AI = CS.arg_begin();
|
|
|
|
DSNodeHandle H = getValueDest(**AI++);
|
|
|
|
if (DSNode *N = H.getNode()) {
|
|
|
|
N->setReadMarker()->setModifiedMarker(); // Reads/Writes buffer
|
|
|
|
const Type *ArgTy = F->getFunctionType()->getParamType(0);
|
|
|
|
if (const PointerType *PTy = dyn_cast<PointerType>(ArgTy))
|
|
|
|
N->mergeTypeInfo(PTy->getElementType(), H.getOffset());
|
|
|
|
}
|
|
|
|
H.mergeWith(getValueDest(*CS.getInstruction())); // Returns buffer
|
|
|
|
|
|
|
|
H = getValueDest(**AI); // Reads delimiter
|
|
|
|
if (DSNode *N = H.getNode()) {
|
|
|
|
N->setReadMarker();
|
|
|
|
const Type *ArgTy = F->getFunctionType()->getParamType(1);
|
|
|
|
if (const PointerType *PTy = dyn_cast<PointerType>(ArgTy))
|
|
|
|
N->mergeTypeInfo(PTy->getElementType(), H.getOffset());
|
|
|
|
}
|
|
|
|
return;
|
2004-02-26 03:43:08 +00:00
|
|
|
} else if (F->getName() == "strchr" || F->getName() == "strrchr" ||
|
|
|
|
F->getName() == "strstr") {
|
|
|
|
// These read their arguments, and return the first one
|
2004-02-25 17:43:20 +00:00
|
|
|
DSNodeHandle H = getValueDest(**CS.arg_begin());
|
2004-02-26 03:43:08 +00:00
|
|
|
H.mergeWith(getValueDest(*CS.getInstruction())); // Returns buffer
|
|
|
|
|
|
|
|
for (CallSite::arg_iterator AI = CS.arg_begin(), E = CS.arg_end();
|
|
|
|
AI != E; ++AI)
|
|
|
|
if (isPointerType((*AI)->getType()))
|
|
|
|
if (DSNode *N = getValueDest(**AI).getNode())
|
|
|
|
N->setReadMarker();
|
2005-04-21 21:13:18 +00:00
|
|
|
|
2004-02-25 17:43:20 +00:00
|
|
|
if (DSNode *N = H.getNode())
|
|
|
|
N->setReadMarker();
|
|
|
|
return;
|
2004-11-08 21:08:28 +00:00
|
|
|
} else if (F->getName() == "__assert_fail") {
|
|
|
|
for (CallSite::arg_iterator AI = CS.arg_begin(), E = CS.arg_end();
|
|
|
|
AI != E; ++AI)
|
|
|
|
if (isPointerType((*AI)->getType()))
|
|
|
|
if (DSNode *N = getValueDest(**AI).getNode())
|
|
|
|
N->setReadMarker();
|
|
|
|
return;
|
2004-02-25 23:06:40 +00:00
|
|
|
} else if (F->getName() == "modf" && CS.arg_end()-CS.arg_begin() == 2) {
|
|
|
|
// This writes its second argument, and forces it to double.
|
2004-12-08 16:22:26 +00:00
|
|
|
CallSite::arg_iterator compit = CS.arg_end();
|
|
|
|
DSNodeHandle H = getValueDest(**--compit);
|
2004-02-25 23:06:40 +00:00
|
|
|
if (DSNode *N = H.getNode()) {
|
|
|
|
N->setModifiedMarker();
|
|
|
|
N->mergeTypeInfo(Type::DoubleTy, H.getOffset());
|
|
|
|
}
|
|
|
|
return;
|
2004-02-13 21:21:48 +00:00
|
|
|
} else {
|
2004-02-16 22:57:19 +00:00
|
|
|
// Unknown function, warn if it returns a pointer type or takes a
|
|
|
|
// pointer argument.
|
|
|
|
bool Warn = isPointerType(CS.getInstruction()->getType());
|
|
|
|
if (!Warn)
|
|
|
|
for (CallSite::arg_iterator I = CS.arg_begin(), E = CS.arg_end();
|
|
|
|
I != E; ++I)
|
|
|
|
if (isPointerType((*I)->getType())) {
|
|
|
|
Warn = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (Warn)
|
|
|
|
std::cerr << "WARNING: Call to unknown external function '"
|
|
|
|
<< F->getName() << "' will cause pessimistic results!\n";
|
2004-02-13 16:09:54 +00:00
|
|
|
}
|
2003-09-20 16:50:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-07-11 20:32:02 +00:00
|
|
|
// Set up the return value...
|
2002-10-21 02:08:03 +00:00
|
|
|
DSNodeHandle RetVal;
|
2003-09-20 16:34:13 +00:00
|
|
|
Instruction *I = CS.getInstruction();
|
|
|
|
if (isPointerType(I->getType()))
|
|
|
|
RetVal = getValueDest(*I);
|
2002-07-11 20:32:02 +00:00
|
|
|
|
2004-02-26 22:07:22 +00:00
|
|
|
DSNode *CalleeNode = 0;
|
|
|
|
if (DisableDirectCallOpt || !isa<Function>(Callee)) {
|
|
|
|
CalleeNode = getValueDest(*Callee).getNode();
|
|
|
|
if (CalleeNode == 0) {
|
|
|
|
std::cerr << "WARNING: Program is calling through a null pointer?\n"<< *I;
|
2003-09-24 23:42:58 +00:00
|
|
|
return; // Calling a null pointer?
|
|
|
|
}
|
|
|
|
}
|
2002-10-21 02:08:03 +00:00
|
|
|
|
|
|
|
std::vector<DSNodeHandle> Args;
|
2003-09-20 16:34:13 +00:00
|
|
|
Args.reserve(CS.arg_end()-CS.arg_begin());
|
2002-07-18 00:12:30 +00:00
|
|
|
|
2002-10-21 02:08:03 +00:00
|
|
|
// Calculate the arguments vector...
|
2003-09-20 16:34:13 +00:00
|
|
|
for (CallSite::arg_iterator I = CS.arg_begin(), E = CS.arg_end(); I != E; ++I)
|
|
|
|
if (isPointerType((*I)->getType()))
|
|
|
|
Args.push_back(getValueDest(**I));
|
2002-10-21 02:08:03 +00:00
|
|
|
|
|
|
|
// Add a new function call entry...
|
2004-02-26 22:07:22 +00:00
|
|
|
if (CalleeNode)
|
|
|
|
FunctionCalls->push_back(DSCallSite(CS, RetVal, CalleeNode, Args));
|
2003-02-05 21:59:58 +00:00
|
|
|
else
|
2004-02-26 22:07:22 +00:00
|
|
|
FunctionCalls->push_back(DSCallSite(CS, RetVal, cast<Function>(Callee),
|
2003-09-20 21:48:16 +00:00
|
|
|
Args));
|
2002-07-10 22:38:08 +00:00
|
|
|
}
|
2002-07-18 15:54:42 +00:00
|
|
|
|
2002-12-06 21:17:10 +00:00
|
|
|
void GraphBuilder::visitFreeInst(FreeInst &FI) {
|
|
|
|
// Mark that the node is written to...
|
2004-02-24 22:02:48 +00:00
|
|
|
if (DSNode *N = getValueDest(*FI.getOperand(0)).getNode())
|
|
|
|
N->setModifiedMarker()->setHeapNodeMarker();
|
2002-12-06 21:17:10 +00:00
|
|
|
}
|
|
|
|
|
2002-10-01 22:33:50 +00:00
|
|
|
/// Handle casts...
|
|
|
|
void GraphBuilder::visitCastInst(CastInst &CI) {
|
2002-11-02 00:36:03 +00:00
|
|
|
if (isPointerType(CI.getType()))
|
|
|
|
if (isPointerType(CI.getOperand(0)->getType())) {
|
2004-10-06 19:29:13 +00:00
|
|
|
DSNodeHandle Ptr = getValueDest(*CI.getOperand(0));
|
|
|
|
if (Ptr.getNode() == 0) return;
|
|
|
|
|
2002-11-02 00:36:03 +00:00
|
|
|
// Cast one pointer to the other, just act like a copy instruction
|
2004-10-06 19:29:13 +00:00
|
|
|
setDestTo(CI, Ptr);
|
2002-11-02 00:36:03 +00:00
|
|
|
} 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.
|
|
|
|
//
|
2003-06-19 21:15:11 +00:00
|
|
|
setDestTo(CI, createNode()->setUnknownNodeMarker());
|
2002-11-02 00:36:03 +00:00
|
|
|
}
|
2002-10-01 22:33:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-04 00:59:50 +00:00
|
|
|
// 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));
|
|
|
|
|
2004-10-30 04:22:45 +00:00
|
|
|
if (DSNode *N = CurNode.getNode())
|
|
|
|
N->setUnknownNodeMarker();
|
2003-02-04 00:59:50 +00:00
|
|
|
}
|
|
|
|
|
2002-10-01 22:33:50 +00:00
|
|
|
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// LocalDataStructures Implementation
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2003-09-20 21:48:16 +00:00
|
|
|
// MergeConstantInitIntoNode - Merge the specified constant into the node
|
|
|
|
// pointed to by NH.
|
|
|
|
void GraphBuilder::MergeConstantInitIntoNode(DSNodeHandle &NH, Constant *C) {
|
|
|
|
// Ensure a type-record exists...
|
2004-10-30 04:22:45 +00:00
|
|
|
DSNode *NHN = NH.getNode();
|
|
|
|
NHN->mergeTypeInfo(C->getType(), NH.getOffset());
|
2003-09-20 21:48:16 +00:00
|
|
|
|
|
|
|
if (C->getType()->isFirstClassType()) {
|
|
|
|
if (isPointerType(C->getType()))
|
|
|
|
// Avoid adding edges from null, or processing non-"pointer" stores
|
|
|
|
NH.addEdgeTo(getValueDest(*C));
|
|
|
|
return;
|
|
|
|
}
|
2003-11-02 22:27:28 +00:00
|
|
|
|
|
|
|
const TargetData &TD = NH.getNode()->getTargetData();
|
|
|
|
|
2003-09-20 21:48:16 +00:00
|
|
|
if (ConstantArray *CA = dyn_cast<ConstantArray>(C)) {
|
|
|
|
for (unsigned i = 0, e = CA->getNumOperands(); i != e; ++i)
|
|
|
|
// We don't currently do any indexing for arrays...
|
|
|
|
MergeConstantInitIntoNode(NH, cast<Constant>(CA->getOperand(i)));
|
|
|
|
} else if (ConstantStruct *CS = dyn_cast<ConstantStruct>(C)) {
|
|
|
|
const StructLayout *SL = TD.getStructLayout(CS->getType());
|
|
|
|
for (unsigned i = 0, e = CS->getNumOperands(); i != e; ++i) {
|
2004-10-30 04:22:45 +00:00
|
|
|
DSNode *NHN = NH.getNode();
|
2005-01-12 04:51:37 +00:00
|
|
|
DSNodeHandle NewNH(NHN, NH.getOffset()+(unsigned)SL->MemberOffsets[i]);
|
2003-09-20 21:48:16 +00:00
|
|
|
MergeConstantInitIntoNode(NewNH, cast<Constant>(CS->getOperand(i)));
|
|
|
|
}
|
2004-10-26 16:23:03 +00:00
|
|
|
} else if (isa<ConstantAggregateZero>(C) || isa<UndefValue>(C)) {
|
2004-02-15 05:53:42 +00:00
|
|
|
// Noop
|
2003-09-20 21:48:16 +00:00
|
|
|
} else {
|
|
|
|
assert(0 && "Unknown constant type!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void GraphBuilder::mergeInGlobalInitializer(GlobalVariable *GV) {
|
|
|
|
assert(!GV->isExternal() && "Cannot merge in external global!");
|
|
|
|
// Get a node handle to the global node and merge the initializer into it.
|
|
|
|
DSNodeHandle NH = getValueDest(*GV);
|
|
|
|
MergeConstantInitIntoNode(NH, GV->getInitializer());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-03-20 03:32:35 +00:00
|
|
|
/// BuildGlobalECs - Look at all of the nodes in the globals graph. If any node
|
|
|
|
/// contains multiple globals, DSA will never, ever, be able to tell the globals
|
|
|
|
/// apart. Instead of maintaining this information in all of the graphs
|
|
|
|
/// throughout the entire program, store only a single global (the "leader") in
|
|
|
|
/// the graphs, and build equivalence classes for the rest of the globals.
|
|
|
|
static void BuildGlobalECs(DSGraph &GG, std::set<GlobalValue*> &ECGlobals) {
|
|
|
|
DSScalarMap &SM = GG.getScalarMap();
|
|
|
|
EquivalenceClasses<GlobalValue*> &GlobalECs = SM.getGlobalECs();
|
|
|
|
for (DSGraph::node_iterator I = GG.node_begin(), E = GG.node_end();
|
|
|
|
I != E; ++I) {
|
|
|
|
if (I->getGlobalsList().size() <= 1) continue;
|
|
|
|
|
|
|
|
// First, build up the equivalence set for this block of globals.
|
|
|
|
const std::vector<GlobalValue*> &GVs = I->getGlobalsList();
|
|
|
|
GlobalValue *First = GVs[0];
|
|
|
|
for (unsigned i = 1, e = GVs.size(); i != e; ++i)
|
|
|
|
GlobalECs.unionSets(First, GVs[i]);
|
2005-04-21 21:13:18 +00:00
|
|
|
|
2005-03-20 03:32:35 +00:00
|
|
|
// Next, get the leader element.
|
|
|
|
assert(First == GlobalECs.getLeaderValue(First) &&
|
|
|
|
"First did not end up being the leader?");
|
2005-04-21 21:13:18 +00:00
|
|
|
|
2005-03-20 03:32:35 +00:00
|
|
|
// Next, remove all globals from the scalar map that are not the leader.
|
|
|
|
assert(GVs[0] == First && "First had to be at the front!");
|
|
|
|
for (unsigned i = 1, e = GVs.size(); i != e; ++i) {
|
|
|
|
ECGlobals.insert(GVs[i]);
|
|
|
|
SM.erase(SM.find(GVs[i]));
|
|
|
|
}
|
2005-04-21 21:13:18 +00:00
|
|
|
|
2005-03-20 03:32:35 +00:00
|
|
|
// Finally, change the global node to only contain the leader.
|
|
|
|
I->clearGlobals();
|
|
|
|
I->addGlobal(First);
|
|
|
|
}
|
2005-04-21 21:13:18 +00:00
|
|
|
|
2005-03-20 03:32:35 +00:00
|
|
|
DEBUG(GG.AssertGraphOK());
|
|
|
|
}
|
|
|
|
|
|
|
|
/// EliminateUsesOfECGlobals - Once we have determined that some globals are in
|
|
|
|
/// really just equivalent to some other globals, remove the globals from the
|
|
|
|
/// specified DSGraph (if present), and merge any nodes with their leader nodes.
|
|
|
|
static void EliminateUsesOfECGlobals(DSGraph &G,
|
|
|
|
const std::set<GlobalValue*> &ECGlobals) {
|
|
|
|
DSScalarMap &SM = G.getScalarMap();
|
|
|
|
EquivalenceClasses<GlobalValue*> &GlobalECs = SM.getGlobalECs();
|
|
|
|
|
|
|
|
bool MadeChange = false;
|
|
|
|
for (DSScalarMap::global_iterator GI = SM.global_begin(), E = SM.global_end();
|
|
|
|
GI != E; ) {
|
|
|
|
GlobalValue *GV = *GI++;
|
|
|
|
if (!ECGlobals.count(GV)) continue;
|
|
|
|
|
|
|
|
const DSNodeHandle &GVNH = SM[GV];
|
|
|
|
assert(!GVNH.isNull() && "Global has null NH!?");
|
|
|
|
|
|
|
|
// Okay, this global is in some equivalence class. Start by finding the
|
|
|
|
// leader of the class.
|
|
|
|
GlobalValue *Leader = GlobalECs.getLeaderValue(GV);
|
|
|
|
|
|
|
|
// If the leader isn't already in the graph, insert it into the node
|
|
|
|
// corresponding to GV.
|
|
|
|
if (!SM.global_count(Leader)) {
|
|
|
|
GVNH.getNode()->addGlobal(Leader);
|
|
|
|
SM[Leader] = GVNH;
|
|
|
|
} else {
|
|
|
|
// Otherwise, the leader is in the graph, make sure the nodes are the
|
|
|
|
// merged in the specified graph.
|
|
|
|
const DSNodeHandle &LNH = SM[Leader];
|
|
|
|
if (LNH.getNode() != GVNH.getNode())
|
|
|
|
LNH.mergeWith(GVNH);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Next step, remove the global from the DSNode.
|
|
|
|
GVNH.getNode()->removeGlobal(GV);
|
|
|
|
|
|
|
|
// Finally, remove the global from the ScalarMap.
|
|
|
|
SM.erase(GV);
|
|
|
|
MadeChange = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
DEBUG(if(MadeChange) G.AssertGraphOK());
|
|
|
|
}
|
|
|
|
|
2004-09-20 04:48:05 +00:00
|
|
|
bool LocalDataStructures::runOnModule(Module &M) {
|
2003-11-02 22:27:28 +00:00
|
|
|
const TargetData &TD = getAnalysis<TargetData>();
|
2002-11-09 21:12:07 +00:00
|
|
|
|
Create an equivalence class of global variables that DSA will never be able
to tell apart anyway, and only track the leader for of these equivalence
classes in our graphs.
This dramatically reduces the number of GlobalValue*'s that appear in scalar
maps, which A) reduces memory usage, by eliminating many many scalarmap entries
and B) reduces time for operations that need to execute an operation for each
global in the scalar map.
As an example, this reduces the memory used to analyze 176.gcc from 1GB to
511MB, which (while it's still way too much) is better because it doesn't hit
swap anymore. On eon, this shrinks the local graphs from 14MB to 6.8MB,
shrinks the bu+td graphs of povray from 50M to 40M, shrinks the TD graphs of
130.li from 8.8M to 3.6M, etc.
This change also speeds up DSA on large programs where this makes a big
difference. For example, 130.li goes from 1.17s -> 0.56s, 134.perl goes
from 2.14 -> 0.93s, povray goes from 15.63s->7.99s (!!!).
This also apparently either fixes the problem that caused DSA to crash on
perlbmk and gcc, or it hides it, because DSA now works on these. These
both take entirely too much time in the TD pass (147s for perl, 538s for
gcc, vs 7.67/5.9s in the bu pass for either one), but this is a known
problem that I'll deal with later.
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@20696 91177308-0d34-0410-b5e6-96231b3b80d8
2005-03-19 22:23:45 +00:00
|
|
|
// First step, build the globals graph.
|
|
|
|
GlobalsGraph = new DSGraph(GlobalECs, TD);
|
2004-02-25 23:31:02 +00:00
|
|
|
{
|
|
|
|
GraphBuilder GGB(*GlobalsGraph);
|
2005-04-21 21:13:18 +00:00
|
|
|
|
Create an equivalence class of global variables that DSA will never be able
to tell apart anyway, and only track the leader for of these equivalence
classes in our graphs.
This dramatically reduces the number of GlobalValue*'s that appear in scalar
maps, which A) reduces memory usage, by eliminating many many scalarmap entries
and B) reduces time for operations that need to execute an operation for each
global in the scalar map.
As an example, this reduces the memory used to analyze 176.gcc from 1GB to
511MB, which (while it's still way too much) is better because it doesn't hit
swap anymore. On eon, this shrinks the local graphs from 14MB to 6.8MB,
shrinks the bu+td graphs of povray from 50M to 40M, shrinks the TD graphs of
130.li from 8.8M to 3.6M, etc.
This change also speeds up DSA on large programs where this makes a big
difference. For example, 130.li goes from 1.17s -> 0.56s, 134.perl goes
from 2.14 -> 0.93s, povray goes from 15.63s->7.99s (!!!).
This also apparently either fixes the problem that caused DSA to crash on
perlbmk and gcc, or it hides it, because DSA now works on these. These
both take entirely too much time in the TD pass (147s for perl, 538s for
gcc, vs 7.67/5.9s in the bu pass for either one), but this is a known
problem that I'll deal with later.
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@20696 91177308-0d34-0410-b5e6-96231b3b80d8
2005-03-19 22:23:45 +00:00
|
|
|
// Add initializers for all of the globals to the globals graph.
|
|
|
|
for (Module::global_iterator I = M.global_begin(), E = M.global_end();
|
|
|
|
I != E; ++I)
|
2004-02-25 23:31:02 +00:00
|
|
|
if (!I->isExternal())
|
|
|
|
GGB.mergeInGlobalInitializer(I);
|
|
|
|
}
|
|
|
|
|
Create an equivalence class of global variables that DSA will never be able
to tell apart anyway, and only track the leader for of these equivalence
classes in our graphs.
This dramatically reduces the number of GlobalValue*'s that appear in scalar
maps, which A) reduces memory usage, by eliminating many many scalarmap entries
and B) reduces time for operations that need to execute an operation for each
global in the scalar map.
As an example, this reduces the memory used to analyze 176.gcc from 1GB to
511MB, which (while it's still way too much) is better because it doesn't hit
swap anymore. On eon, this shrinks the local graphs from 14MB to 6.8MB,
shrinks the bu+td graphs of povray from 50M to 40M, shrinks the TD graphs of
130.li from 8.8M to 3.6M, etc.
This change also speeds up DSA on large programs where this makes a big
difference. For example, 130.li goes from 1.17s -> 0.56s, 134.perl goes
from 2.14 -> 0.93s, povray goes from 15.63s->7.99s (!!!).
This also apparently either fixes the problem that caused DSA to crash on
perlbmk and gcc, or it hides it, because DSA now works on these. These
both take entirely too much time in the TD pass (147s for perl, 538s for
gcc, vs 7.67/5.9s in the bu pass for either one), but this is a known
problem that I'll deal with later.
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@20696 91177308-0d34-0410-b5e6-96231b3b80d8
2005-03-19 22:23:45 +00:00
|
|
|
// Next step, iterate through the nodes in the globals graph, unioning
|
|
|
|
// together the globals into equivalence classes.
|
2005-03-20 03:32:35 +00:00
|
|
|
std::set<GlobalValue*> ECGlobals;
|
|
|
|
BuildGlobalECs(*GlobalsGraph, ECGlobals);
|
|
|
|
DEBUG(std::cerr << "Eliminating " << ECGlobals.size() << " EC Globals!\n");
|
|
|
|
ECGlobals.clear();
|
Create an equivalence class of global variables that DSA will never be able
to tell apart anyway, and only track the leader for of these equivalence
classes in our graphs.
This dramatically reduces the number of GlobalValue*'s that appear in scalar
maps, which A) reduces memory usage, by eliminating many many scalarmap entries
and B) reduces time for operations that need to execute an operation for each
global in the scalar map.
As an example, this reduces the memory used to analyze 176.gcc from 1GB to
511MB, which (while it's still way too much) is better because it doesn't hit
swap anymore. On eon, this shrinks the local graphs from 14MB to 6.8MB,
shrinks the bu+td graphs of povray from 50M to 40M, shrinks the TD graphs of
130.li from 8.8M to 3.6M, etc.
This change also speeds up DSA on large programs where this makes a big
difference. For example, 130.li goes from 1.17s -> 0.56s, 134.perl goes
from 2.14 -> 0.93s, povray goes from 15.63s->7.99s (!!!).
This also apparently either fixes the problem that caused DSA to crash on
perlbmk and gcc, or it hides it, because DSA now works on these. These
both take entirely too much time in the TD pass (147s for perl, 538s for
gcc, vs 7.67/5.9s in the bu pass for either one), but this is a known
problem that I'll deal with later.
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@20696 91177308-0d34-0410-b5e6-96231b3b80d8
2005-03-19 22:23:45 +00:00
|
|
|
|
2002-11-09 21:12:07 +00:00
|
|
|
// Calculate all of the graphs...
|
|
|
|
for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I)
|
|
|
|
if (!I->isExternal())
|
Create an equivalence class of global variables that DSA will never be able
to tell apart anyway, and only track the leader for of these equivalence
classes in our graphs.
This dramatically reduces the number of GlobalValue*'s that appear in scalar
maps, which A) reduces memory usage, by eliminating many many scalarmap entries
and B) reduces time for operations that need to execute an operation for each
global in the scalar map.
As an example, this reduces the memory used to analyze 176.gcc from 1GB to
511MB, which (while it's still way too much) is better because it doesn't hit
swap anymore. On eon, this shrinks the local graphs from 14MB to 6.8MB,
shrinks the bu+td graphs of povray from 50M to 40M, shrinks the TD graphs of
130.li from 8.8M to 3.6M, etc.
This change also speeds up DSA on large programs where this makes a big
difference. For example, 130.li goes from 1.17s -> 0.56s, 134.perl goes
from 2.14 -> 0.93s, povray goes from 15.63s->7.99s (!!!).
This also apparently either fixes the problem that caused DSA to crash on
perlbmk and gcc, or it hides it, because DSA now works on these. These
both take entirely too much time in the TD pass (147s for perl, 538s for
gcc, vs 7.67/5.9s in the bu pass for either one), but this is a known
problem that I'll deal with later.
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@20696 91177308-0d34-0410-b5e6-96231b3b80d8
2005-03-19 22:23:45 +00:00
|
|
|
DSInfo.insert(std::make_pair(I, new DSGraph(GlobalECs, TD, *I,
|
|
|
|
GlobalsGraph)));
|
2003-09-20 21:48:16 +00:00
|
|
|
|
2004-02-08 01:51:48 +00:00
|
|
|
GlobalsGraph->removeTriviallyDeadNodes();
|
2003-09-20 21:48:16 +00:00
|
|
|
GlobalsGraph->markIncompleteNodes(DSGraph::MarkFormalArgs);
|
2005-03-20 03:32:35 +00:00
|
|
|
|
|
|
|
// Now that we've computed all of the graphs, and merged all of the info into
|
|
|
|
// the globals graph, see if we have further constrained the globals in the
|
|
|
|
// program if so, update GlobalECs and remove the extraneous globals from the
|
|
|
|
// program.
|
|
|
|
BuildGlobalECs(*GlobalsGraph, ECGlobals);
|
|
|
|
if (!ECGlobals.empty()) {
|
|
|
|
DEBUG(std::cerr << "Eliminating " << ECGlobals.size() << " EC Globals!\n");
|
|
|
|
for (hash_map<Function*, DSGraph*>::iterator I = DSInfo.begin(),
|
|
|
|
E = DSInfo.end(); I != E; ++I)
|
|
|
|
EliminateUsesOfECGlobals(*I->second, ECGlobals);
|
|
|
|
}
|
|
|
|
|
2002-11-09 21:12:07 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2002-10-01 22:33:50 +00:00
|
|
|
// releaseMemory - If the pass pipeline is done with this pass, we can release
|
|
|
|
// our memory... here...
|
2002-07-18 15:54:42 +00:00
|
|
|
//
|
2002-10-01 22:33:50 +00:00
|
|
|
void LocalDataStructures::releaseMemory() {
|
2003-06-30 04:53:27 +00:00
|
|
|
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;
|
|
|
|
}
|
2002-10-01 22:33:50 +00:00
|
|
|
|
|
|
|
// Empty map so next time memory is released, data structures are not
|
|
|
|
// re-deleted.
|
|
|
|
DSInfo.clear();
|
2002-11-09 20:01:01 +00:00
|
|
|
delete GlobalsGraph;
|
|
|
|
GlobalsGraph = 0;
|
2002-10-01 22:33:50 +00:00
|
|
|
}
|
2003-11-11 22:41:34 +00:00
|
|
|
|