Newer
Older
//===- DataStructure.cpp - Implement the core data structure analysis -----===//
//
// The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// This file implements the core data structure functionality.
//
//===----------------------------------------------------------------------===//
#include "llvm/Analysis/DSGraph.h"
#include "llvm/Function.h"
#include "llvm/DerivedTypes.h"
#include "llvm/Target/TargetData.h"
#include "llvm/Assembly/Writer.h"
Chris Lattner
committed
#include "Support/CommandLine.h"
#include "Support/STLExtras.h"
#include "Support/Statistic.h"
#include <algorithm>
namespace {
Statistic<> NumFolds ("dsa", "Number of nodes completely folded");
Statistic<> NumCallNodesMerged("dsa", "Number of call nodes merged");
Statistic<> NumNodeAllocated ("dsa", "Number of nodes allocated");
Statistic<> NumDNE ("dsa", "Number of nodes removed by reachability");
Statistic<> NumTrivialDNE ("dsa", "Number of nodes trivially removed");
Statistic<> NumTrivialGlobalDNE("dsa", "Number of globals trivially removed");
};
#if 1
#define TIME_REGION(VARNAME, DESC) \
NamedRegionTimer VARNAME(DESC)
#else
#define TIME_REGION(VARNAME, DESC)
#endif
DSNode *DSNodeHandle::HandleForwarding() const {
assert(N->isForwarding() && "Can only be invoked if forwarding!");
// Handle node forwarding here!
DSNode *Next = N->ForwardNH.getNode(); // Cause recursive shrinkage
Offset += N->ForwardNH.getOffset();
if (--N->NumReferrers == 0) {
// Removing the last referrer to the node, sever the forwarding link
N->stopForwarding();
}
N = Next;
N->NumReferrers++;
if (N->Size <= Offset) {
assert(N->Size <= 1 && "Forwarded to shrunk but not collapsed node?");
Offset = 0;
}
return N;
}
//===----------------------------------------------------------------------===//
// DSNode Implementation
//===----------------------------------------------------------------------===//
DSNode::DSNode(const Type *T, DSGraph *G)
: NumReferrers(0), Size(0), ParentGraph(G), Ty(Type::VoidTy), NodeType(0) {
Chris Lattner
committed
// Add the type entry if it is specified...
if (T) mergeTypeInfo(T, 0);
Chris Lattner
committed
if (G) G->addNode(this);
Chris Lattner
committed
++NumNodeAllocated;
}
// DSNode copy constructor... do not copy over the referrers list!
Chris Lattner
committed
DSNode::DSNode(const DSNode &N, DSGraph *G, bool NullLinks)
: NumReferrers(0), Size(N.Size), ParentGraph(G),
Chris Lattner
committed
Ty(N.Ty), Globals(N.Globals), NodeType(N.NodeType) {
if (!NullLinks)
Links = N.Links;
else
Links.resize(N.Links.size()); // Create the appropriate number of null links
Chris Lattner
committed
++NumNodeAllocated;
}
Chris Lattner
committed
/// getTargetData - Get the target data object used to construct this node.
///
const TargetData &DSNode::getTargetData() const {
return ParentGraph->getTargetData();
}
void DSNode::assertOK() const {
assert((Ty != Type::VoidTy ||
Ty == Type::VoidTy && (Size == 0 ||
(NodeType & DSNode::Array))) &&
"Node not OK!");
assert(ParentGraph && "Node has no parent?");
const DSScalarMap &SM = ParentGraph->getScalarMap();
for (unsigned i = 0, e = Globals.size(); i != e; ++i) {
assert(SM.count(Globals[i]));
assert(SM.find(Globals[i])->second.getNode() == this);
}
}
/// forwardNode - Mark this node as being obsolete, and all references to it
/// should be forwarded to the specified node and offset.
///
void DSNode::forwardNode(DSNode *To, unsigned Offset) {
assert(this != To && "Cannot forward a node to itself!");
assert(ForwardNH.isNull() && "Already forwarding from this node!");
if (To->Size <= 1) Offset = 0;
assert((Offset < To->Size || (Offset == To->Size && Offset == 0)) &&
"Forwarded offset is wrong!");
ForwardNH.setNode(To);
ForwardNH.setOffset(Offset);
NodeType = DEAD;
Size = 0;
Ty = Type::VoidTy;
// Remove this node from the parent graph's Nodes list.
ParentGraph->unlinkNode(this);
ParentGraph = 0;
// addGlobal - Add an entry for a global value to the Globals list. This also
// marks the node with the 'G' flag if it does not already have it.
//
void DSNode::addGlobal(GlobalValue *GV) {
// Keep the list sorted.
std::lower_bound(Globals.begin(), Globals.end(), GV);
if (I == Globals.end() || *I != GV) {
//assert(GV->getType()->getElementType() == Ty);
Globals.insert(I, GV);
NodeType |= GlobalNode;
}
Chris Lattner
committed
/// foldNodeCompletely - If we determine that this node has some funny
/// behavior happening to it that we cannot represent, we fold it down to a
/// single, completely pessimistic, node. This node is represented as a
/// single byte with a single TypeEntry of "void".
///
void DSNode::foldNodeCompletely() {
if (isNodeCompletelyFolded()) return; // If this node is already folded...
++NumFolds;
Chris Lattner
committed
// If this node has a size that is <= 1, we don't need to create a forwarding
// node.
if (getSize() <= 1) {
NodeType |= DSNode::Array;
Ty = Type::VoidTy;
Size = 1;
assert(Links.size() <= 1 && "Size is 1, but has more links?");
Links.resize(1);
} else {
Chris Lattner
committed
// Create the node we are going to forward to. This is required because
// some referrers may have an offset that is > 0. By forcing them to
// forward, the forwarder has the opportunity to correct the offset.
DSNode *DestNode = new DSNode(0, ParentGraph);
DestNode->NodeType = NodeType|DSNode::Array;
DestNode->Ty = Type::VoidTy;
DestNode->Size = 1;
DestNode->Globals.swap(Globals);
// Start forwarding to the destination node...
forwardNode(DestNode, 0);
if (!Links.empty()) {
DestNode->Links.reserve(1);
DSNodeHandle NH(DestNode);
DestNode->Links.push_back(Links[0]);
// If we have links, merge all of our outgoing links together...
for (unsigned i = Links.size()-1; i != 0; --i)
NH.getNode()->Links[0].mergeWith(Links[i]);
Links.clear();
} else {
DestNode->Links.resize(1);
}
}
Chris Lattner
committed
}
Chris Lattner
committed
/// isNodeCompletelyFolded - Return true if this node has been completely
/// folded down to something that can never be expanded, effectively losing
/// all of the field sensitivity that may be present in the node.
///
bool DSNode::isNodeCompletelyFolded() const {
return getSize() == 1 && Ty == Type::VoidTy && isArray();
Chris Lattner
committed
}
namespace {
/// TypeElementWalker Class - Used for implementation of physical subtyping...
///
class TypeElementWalker {
struct StackState {
const Type *Ty;
unsigned Offset;
unsigned Idx;
StackState(const Type *T, unsigned Off = 0)
: Ty(T), Offset(Off), Idx(0) {}
};
std::vector<StackState> Stack;
Chris Lattner
committed
const TargetData &TD;
Chris Lattner
committed
TypeElementWalker(const Type *T, const TargetData &td) : TD(td) {
Stack.push_back(T);
StepToLeaf();
}
bool isDone() const { return Stack.empty(); }
const Type *getCurrentType() const { return Stack.back().Ty; }
unsigned getCurrentOffset() const { return Stack.back().Offset; }
void StepToNextType() {
PopStackAndAdvance();
StepToLeaf();
}
private:
/// PopStackAndAdvance - Pop the current element off of the stack and
/// advance the underlying element to the next contained member.
void PopStackAndAdvance() {
assert(!Stack.empty() && "Cannot pop an empty stack!");
Stack.pop_back();
while (!Stack.empty()) {
StackState &SS = Stack.back();
if (const StructType *ST = dyn_cast<StructType>(SS.Ty)) {
++SS.Idx;
Chris Lattner
committed
if (SS.Idx != ST->getNumElements()) {
const StructLayout *SL = TD.getStructLayout(ST);
SS.Offset += SL->MemberOffsets[SS.Idx]-SL->MemberOffsets[SS.Idx-1];
return;
}
Stack.pop_back(); // At the end of the structure
} else {
const ArrayType *AT = cast<ArrayType>(SS.Ty);
++SS.Idx;
if (SS.Idx != AT->getNumElements()) {
SS.Offset += TD.getTypeSize(AT->getElementType());
return;
}
Stack.pop_back(); // At the end of the array
}
}
}
/// StepToLeaf - Used by physical subtyping to move to the first leaf node
/// on the type stack.
void StepToLeaf() {
if (Stack.empty()) return;
while (!Stack.empty() && !Stack.back().Ty->isFirstClassType()) {
StackState &SS = Stack.back();
if (const StructType *ST = dyn_cast<StructType>(SS.Ty)) {
Chris Lattner
committed
if (ST->getNumElements() == 0) {
assert(SS.Idx == 0);
PopStackAndAdvance();
} else {
// Step into the structure...
Chris Lattner
committed
assert(SS.Idx < ST->getNumElements());
const StructLayout *SL = TD.getStructLayout(ST);
Chris Lattner
committed
Stack.push_back(StackState(ST->getElementType(SS.Idx),
SS.Offset+SL->MemberOffsets[SS.Idx]));
}
} else {
const ArrayType *AT = cast<ArrayType>(SS.Ty);
if (AT->getNumElements() == 0) {
assert(SS.Idx == 0);
PopStackAndAdvance();
} else {
// Step into the array...
assert(SS.Idx < AT->getNumElements());
Stack.push_back(StackState(AT->getElementType(),
SS.Offset+SS.Idx*
TD.getTypeSize(AT->getElementType())));
}
}
}
}
};
} // end anonymous namespace
/// ElementTypesAreCompatible - Check to see if the specified types are
/// "physically" compatible. If so, return true, else return false. We only
/// have to check the fields in T1: T2 may be larger than T1. If AllowLargerT1
/// is true, then we also allow a larger T1.
static bool ElementTypesAreCompatible(const Type *T1, const Type *T2,
Chris Lattner
committed
bool AllowLargerT1, const TargetData &TD){
TypeElementWalker T1W(T1, TD), T2W(T2, TD);
while (!T1W.isDone() && !T2W.isDone()) {
if (T1W.getCurrentOffset() != T2W.getCurrentOffset())
return false;
const Type *T1 = T1W.getCurrentType();
const Type *T2 = T2W.getCurrentType();
if (T1 != T2 && !T1->isLosslesslyConvertibleTo(T2))
return false;
T1W.StepToNextType();
T2W.StepToNextType();
}
return AllowLargerT1 || T1W.isDone();
/// mergeTypeInfo - This method merges the specified type into the current node
/// at the specified offset. This may update the current node's type record if
/// this gives more information to the node, it may do nothing to the node if
/// this information is already known, or it may merge the node completely (and
/// return true) if the information is incompatible with what is already known.
///
/// This method returns true if the node is completely folded, otherwise false.
///
Chris Lattner
committed
bool DSNode::mergeTypeInfo(const Type *NewTy, unsigned Offset,
bool FoldIfIncompatible) {
Chris Lattner
committed
const TargetData &TD = getTargetData();
// Check to make sure the Size member is up-to-date. Size can be one of the
// following:
// Size = 0, Ty = Void: Nothing is known about this node.
// Size = 0, Ty = FnTy: FunctionPtr doesn't have a size, so we use zero
// Size = 1, Ty = Void, Array = 1: The node is collapsed
// Otherwise, sizeof(Ty) = Size
//
assert(((Size == 0 && Ty == Type::VoidTy && !isArray()) ||
(Size == 0 && !Ty->isSized() && !isArray()) ||
(Size == 1 && Ty == Type::VoidTy && isArray()) ||
(Size == 0 && !Ty->isSized() && !isArray()) ||
(TD.getTypeSize(Ty) == Size)) &&
"Size member of DSNode doesn't match the type structure!");
assert(NewTy != Type::VoidTy && "Cannot merge void type into DSNode!");
return false; // This should be a common case, handle it efficiently
// Return true immediately if the node is completely folded.
if (isNodeCompletelyFolded()) return true;
Chris Lattner
committed
// If this is an array type, eliminate the outside arrays because they won't
// be used anyway. This greatly reduces the size of large static arrays used
// as global variables, for example.
//
Chris Lattner
committed
while (const ArrayType *AT = dyn_cast<ArrayType>(NewTy)) {
// FIXME: we might want to keep small arrays, but must be careful about
// things like: [2 x [10000 x int*]]
NewTy = AT->getElementType();
Chris Lattner
committed
}
// Figure out how big the new type we're merging in is...
unsigned NewTySize = NewTy->isSized() ? TD.getTypeSize(NewTy) : 0;
// Otherwise check to see if we can fold this type into the current node. If
// we can't, we fold the node completely, if we can, we potentially update our
// internal state.
//
// If this is the first type that this node has seen, just accept it without
// question....
assert(Offset == 0 && !isArray() &&
"Cannot have an offset into a void node!");
Ty = NewTy;
NodeType &= ~Array;
if (WillBeArray) NodeType |= Array;
Size = NewTySize;
// Calculate the number of outgoing links from this node.
Links.resize((Size+DS::PointerSize-1) >> DS::PointerShift);
return false;
}
// Handle node expansion case here...
if (Offset+NewTySize > Size) {
// It is illegal to grow this node if we have treated it as an array of
// objects...
Chris Lattner
committed
if (FoldIfIncompatible) foldNodeCompletely();
return true;
}
if (Offset) { // We could handle this case, but we don't for now...
std::cerr << "UNIMP: Trying to merge a growth type into "
<< "offset != 0: Collapsing!\n";
Chris Lattner
committed
if (FoldIfIncompatible) foldNodeCompletely();
return true;
}
// Okay, the situation is nice and simple, we are trying to merge a type in
// at offset 0 that is bigger than our current type. Implement this by
// switching to the new type and then merge in the smaller one, which should
// hit the other code path here. If the other code path decides it's not
// ok, it will collapse the node as appropriate.
//
const Type *OldTy = Ty;
Ty = NewTy;
NodeType &= ~Array;
if (WillBeArray) NodeType |= Array;
Size = NewTySize;
Chris Lattner
committed
// Must grow links to be the appropriate size...
Links.resize((Size+DS::PointerSize-1) >> DS::PointerShift);
Chris Lattner
committed
// Merge in the old type now... which is guaranteed to be smaller than the
// "current" type.
return mergeTypeInfo(OldTy, 0);
Chris Lattner
committed
}
Chris Lattner
committed
assert(Offset <= Size &&
"Cannot merge something into a part of our type that doesn't exist!");
// Find the section of Ty that NewTy overlaps with... first we find the
// type that starts at offset Offset.
Chris Lattner
committed
//
unsigned O = 0;
while (O < Offset) {
assert(Offset-O < TD.getTypeSize(SubType) && "Offset out of range!");
switch (SubType->getPrimitiveID()) {
case Type::StructTyID: {
const StructType *STy = cast<StructType>(SubType);
const StructLayout &SL = *TD.getStructLayout(STy);
unsigned i = 0, e = SL.MemberOffsets.size();
for (; i+1 < e && SL.MemberOffsets[i+1] <= Offset-O; ++i)
/* empty */;
// The offset we are looking for must be in the i'th element...
Chris Lattner
committed
SubType = STy->getElementType(i);
O += SL.MemberOffsets[i];
break;
}
case Type::ArrayTyID: {
SubType = cast<ArrayType>(SubType)->getElementType();
unsigned ElSize = TD.getTypeSize(SubType);
unsigned Remainder = (Offset-O) % ElSize;
O = Offset-Remainder;
break;
Chris Lattner
committed
}
default:
Chris Lattner
committed
if (FoldIfIncompatible) foldNodeCompletely();
}
}
Chris Lattner
committed
assert(O == Offset && "Could not achieve the correct offset!");
Chris Lattner
committed
// If we found our type exactly, early exit
if (SubType == NewTy) return false;
Chris Lattner
committed
Chris Lattner
committed
// Differing function types don't require us to merge. They are not values anyway.
if (isa<FunctionType>(SubType) &&
isa<FunctionType>(NewTy)) return false;
unsigned SubTypeSize = SubType->isSized() ? TD.getTypeSize(SubType) : 0;
// Ok, we are getting desperate now. Check for physical subtyping, where we
// just require each element in the node to be compatible.
if (NewTySize <= SubTypeSize && NewTySize && NewTySize < 256 &&
SubTypeSize && SubTypeSize < 256 &&
Chris Lattner
committed
ElementTypesAreCompatible(NewTy, SubType, !isArray(), TD))
// Okay, so we found the leader type at the offset requested. Search the list
// of types that starts at this offset. If SubType is currently an array or
// structure, the type desired may actually be the first element of the
// composite type...
//
unsigned PadSize = SubTypeSize; // Size, including pad memory which is ignored
while (SubType != NewTy) {
const Type *NextSubType = 0;
switch (SubType->getPrimitiveID()) {
case Type::StructTyID: {
const StructType *STy = cast<StructType>(SubType);
const StructLayout &SL = *TD.getStructLayout(STy);
if (SL.MemberOffsets.size() > 1)
NextPadSize = SL.MemberOffsets[1];
else
NextPadSize = SubTypeSize;
Chris Lattner
committed
NextSubType = STy->getElementType(0);
NextSubTypeSize = TD.getTypeSize(NextSubType);
break;
case Type::ArrayTyID:
NextSubType = cast<ArrayType>(SubType)->getElementType();
NextSubTypeSize = TD.getTypeSize(NextSubType);
NextPadSize = NextSubTypeSize;
break;
default: ;
// fall out
}
Chris Lattner
committed
if (NextSubType == 0)
break; // In the default case, break out of the loop
Chris Lattner
committed
break; // Don't allow shrinking to a smaller type than NewTySize
SubType = NextSubType;
SubTypeSize = NextSubTypeSize;
Chris Lattner
committed
}
// If we found the type exactly, return it...
if (SubType == NewTy)
return false;
Chris Lattner
committed
// Check to see if we have a compatible, but different type...
if (NewTySize == SubTypeSize) {
// Check to see if this type is obviously convertible... int -> uint f.e.
if (NewTy->isLosslesslyConvertibleTo(SubType))
return false;
Chris Lattner
committed
// Check to see if we have a pointer & integer mismatch going on here,
// loading a pointer as a long, for example.
Chris Lattner
committed
//
if (SubType->isInteger() && isa<PointerType>(NewTy) ||
NewTy->isInteger() && isa<PointerType>(SubType))
return false;
} else if (NewTySize > SubTypeSize && NewTySize <= PadSize) {
// We are accessing the field, plus some structure padding. Ignore the
// structure padding.
return false;
Chris Lattner
committed
}
Module *M = 0;
if (getParentGraph()->getReturnNodes().size())
M = getParentGraph()->getReturnNodes().begin()->first->getParent();
DEBUG(std::cerr << "MergeTypeInfo Folding OrigTy: ";
WriteTypeSymbolic(std::cerr, Ty, M) << "\n due to:";
WriteTypeSymbolic(std::cerr, NewTy, M) << " @ " << Offset << "!\n"
<< "SubType: ";
WriteTypeSymbolic(std::cerr, SubType, M) << "\n\n");
Chris Lattner
committed
if (FoldIfIncompatible) foldNodeCompletely();
return true;
Chris Lattner
committed
}
// addEdgeTo - Add an edge from the current node to the specified node. This
// can cause merging of nodes in the graph.
//
void DSNode::addEdgeTo(unsigned Offset, const DSNodeHandle &NH) {
Chris Lattner
committed
if (NH.isNull()) return; // Nothing to do
DSNodeHandle &ExistingEdge = getLink(Offset);
Chris Lattner
committed
if (!ExistingEdge.isNull()) {
// Merge the two nodes...
ExistingEdge.mergeWith(NH);
} else { // No merging to perform...
setLink(Offset, NH); // Just force a link in there...
}
// MergeSortedVectors - Efficiently merge a vector into another vector where
// duplicates are not allowed and both are sorted. This assumes that 'T's are
// efficiently copyable and have sane comparison semantics.
static void MergeSortedVectors(std::vector<GlobalValue*> &Dest,
const std::vector<GlobalValue*> &Src) {
// By far, the most common cases will be the simple ones. In these cases,
// avoid having to allocate a temporary vector...
//
if (Src.empty()) { // Nothing to merge in...
return;
} else if (Dest.empty()) { // Just copy the result in...
Dest = Src;
} else if (Src.size() == 1) { // Insert a single element...
std::lower_bound(Dest.begin(), Dest.end(), V);
if (I == Dest.end() || *I != Src[0]) // If not already contained...
Dest.insert(I, Src[0]);
} else if (Dest.size() == 1) {
GlobalValue *Tmp = Dest[0]; // Save value in temporary...
Dest = Src; // Copy over list...
std::lower_bound(Dest.begin(), Dest.end(), Tmp);
if (I == Dest.end() || *I != Tmp) // If not already contained...
Dest.insert(I, Tmp);
} else {
// Make a copy to the side of Dest...
// Make space for all of the type entries now...
Dest.resize(Dest.size()+Src.size());
// Merge the two sorted ranges together... into Dest.
std::merge(Old.begin(), Old.end(), Src.begin(), Src.end(), Dest.begin());
// Now erase any duplicate entries that may have accumulated into the
// vectors (because they were in both of the input sets)
Dest.erase(std::unique(Dest.begin(), Dest.end()), Dest.end());
}
}
Chris Lattner
committed
void DSNode::mergeGlobals(const std::vector<GlobalValue*> &RHS) {
MergeSortedVectors(Globals, RHS);
}
Chris Lattner
committed
// MergeNodes - Helper function for DSNode::mergeWith().
// This function does the hard work of merging two nodes, CurNodeH
// and NH after filtering out trivial cases and making sure that
// CurNodeH.offset >= NH.offset.
//
// ***WARNING***
// Since merging may cause either node to go away, we must always
// use the node-handles to refer to the nodes. These node handles are
// automatically updated during merging, so will always provide access
// to the correct node after a merge.
//
void DSNode::MergeNodes(DSNodeHandle& CurNodeH, DSNodeHandle& NH) {
assert(CurNodeH.getOffset() >= NH.getOffset() &&
"This should have been enforced in the caller.");
// Now we know that Offset >= NH.Offset, so convert it so our "Offset" (with
// respect to NH.Offset) is now zero. NOffset is the distance from the base
// of our object that N starts from.
//
unsigned NOffset = CurNodeH.getOffset()-NH.getOffset();
unsigned NSize = NH.getNode()->getSize();
// If the two nodes are of different size, and the smaller node has the array
// bit set, collapse!
if (NSize != CurNodeH.getNode()->getSize()) {
if (NSize < CurNodeH.getNode()->getSize()) {
if (NH.getNode()->isArray())
NH.getNode()->foldNodeCompletely();
} else if (CurNodeH.getNode()->isArray()) {
NH.getNode()->foldNodeCompletely();
}
}
// Merge the type entries of the two nodes together...
if (NH.getNode()->Ty != Type::VoidTy)
CurNodeH.getNode()->mergeTypeInfo(NH.getNode()->Ty, NOffset);
assert(!CurNodeH.getNode()->isDeadNode());
Chris Lattner
committed
Chris Lattner
committed
// If we are merging a node with a completely folded node, then both nodes are
// now completely folded.
//
if (CurNodeH.getNode()->isNodeCompletelyFolded()) {
if (!NH.getNode()->isNodeCompletelyFolded()) {
NH.getNode()->foldNodeCompletely();
assert(NH.getNode() && NH.getOffset() == 0 &&
"folding did not make offset 0?");
NOffset = NH.getOffset();
NSize = NH.getNode()->getSize();
assert(NOffset == 0 && NSize == 1);
Chris Lattner
committed
}
} else if (NH.getNode()->isNodeCompletelyFolded()) {
CurNodeH.getNode()->foldNodeCompletely();
assert(CurNodeH.getNode() && CurNodeH.getOffset() == 0 &&
"folding did not make offset 0?");
NSize = NH.getNode()->getSize();
assert(NOffset == 0 && NSize == 1);
Chris Lattner
committed
}
DSNode *N = NH.getNode();
if (CurNodeH.getNode() == N || N == 0) return;
assert(!CurNodeH.getNode()->isDeadNode());
Chris Lattner
committed
// Merge the NodeType information.
CurNodeH.getNode()->NodeType |= N->NodeType;
// Start forwarding to the new node!
N->forwardNode(CurNodeH.getNode(), NOffset);
assert(!CurNodeH.getNode()->isDeadNode());
// Make all of the outgoing links of N now be outgoing links of CurNodeH.
//
for (unsigned i = 0; i < N->getNumLinks(); ++i) {
DSNodeHandle &Link = N->getLink(i << DS::PointerShift);
if (Link.getNode()) {
// Compute the offset into the current node at which to
// merge this link. In the common case, this is a linear
// relation to the offset in the original node (with
// wrapping), but if the current node gets collapsed due to
// recursive merging, we must make sure to merge in all remaining
// links at offset zero.
unsigned MergeOffset = 0;
DSNode *CN = CurNodeH.getNode();
if (CN->Size != 1)
MergeOffset = ((i << DS::PointerShift)+NOffset) % CN->getSize();
CN->addEdgeTo(MergeOffset, Link);
}
}
// Now that there are no outgoing edges, all of the Links are dead.
N->Links.clear();
// Merge the globals list...
if (!N->Globals.empty()) {
Chris Lattner
committed
CurNodeH.getNode()->mergeGlobals(N->Globals);
// Delete the globals from the old node...
std::vector<GlobalValue*>().swap(N->Globals);
}
// mergeWith - Merge this node and the specified node, moving all links to and
// from the argument node into the current node, deleting the node argument.
// Offset indicates what offset the specified node is to be merged into the
// current node.
//
// The specified node may be a null pointer (in which case, we update it to
// point to this node).
//
void DSNode::mergeWith(const DSNodeHandle &NH, unsigned Offset) {
DSNode *N = NH.getNode();
if (N == this && NH.getOffset() == Offset)
return; // Noop
// If the RHS is a null node, make it point to this node!
if (N == 0) {
NH.mergeWith(DSNodeHandle(this, Offset));
return;
}
assert(!N->isDeadNode() && !isDeadNode());
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
assert(!hasNoReferrers() && "Should not try to fold a useless node!");
if (N == this) {
// We cannot merge two pieces of the same node together, collapse the node
// completely.
DEBUG(std::cerr << "Attempting to merge two chunks of"
<< " the same node together!\n");
foldNodeCompletely();
return;
}
// If both nodes are not at offset 0, make sure that we are merging the node
// at an later offset into the node with the zero offset.
//
if (Offset < NH.getOffset()) {
N->mergeWith(DSNodeHandle(this, Offset), NH.getOffset());
return;
} else if (Offset == NH.getOffset() && getSize() < N->getSize()) {
// If the offsets are the same, merge the smaller node into the bigger node
N->mergeWith(DSNodeHandle(this, Offset), NH.getOffset());
return;
}
// Ok, now we can merge the two nodes. Use a static helper that works with
// two node handles, since "this" may get merged away at intermediate steps.
DSNodeHandle CurNodeH(this, Offset);
DSNodeHandle NHCopy(NH);
DSNode::MergeNodes(CurNodeH, NHCopy);
}
Chris Lattner
committed
//===----------------------------------------------------------------------===//
// ReachabilityCloner Implementation
//===----------------------------------------------------------------------===//
DSNodeHandle ReachabilityCloner::getClonedNH(const DSNodeHandle &SrcNH) {
if (SrcNH.isNull()) return DSNodeHandle();
const DSNode *SN = SrcNH.getNode();
DSNodeHandle &NH = NodeMap[SN];
if (!NH.isNull()) // Node already mapped?
return DSNodeHandle(NH.getNode(), NH.getOffset()+SrcNH.getOffset());
DSNode *DN = new DSNode(*SN, &Dest, true /* Null out all links */);
DN->maskNodeTypes(BitsToKeep);
Chris Lattner
committed
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
// Next, recursively clone all outgoing links as necessary. Note that
// adding these links can cause the node to collapse itself at any time, and
// the current node may be merged with arbitrary other nodes. For this
// reason, we must always go through NH.
DN = 0;
for (unsigned i = 0, e = SN->getNumLinks(); i != e; ++i) {
const DSNodeHandle &SrcEdge = SN->getLink(i << DS::PointerShift);
if (!SrcEdge.isNull()) {
const DSNodeHandle &DestEdge = getClonedNH(SrcEdge);
// Compute the offset into the current node at which to
// merge this link. In the common case, this is a linear
// relation to the offset in the original node (with
// wrapping), but if the current node gets collapsed due to
// recursive merging, we must make sure to merge in all remaining
// links at offset zero.
unsigned MergeOffset = 0;
DSNode *CN = NH.getNode();
if (CN->getSize() != 1)
MergeOffset = ((i << DS::PointerShift)+NH.getOffset()
- SrcNH.getOffset()) %CN->getSize();
CN->addEdgeTo(MergeOffset, DestEdge);
}
}
// If this node contains any globals, make sure they end up in the scalar
// map with the correct offset.
for (DSNode::global_iterator I = SN->global_begin(), E = SN->global_end();
I != E; ++I) {
GlobalValue *GV = *I;
const DSNodeHandle &SrcGNH = Src.getNodeForValue(GV);
DSNodeHandle &DestGNH = NodeMap[SrcGNH.getNode()];
assert(DestGNH.getNode() == NH.getNode() &&"Global mapping inconsistent");
Dest.getNodeForValue(GV).mergeWith(DSNodeHandle(DestGNH.getNode(),
Chris Lattner
committed
if (CloneFlags & DSGraph::UpdateInlinedGlobals)
Dest.getInlinedGlobals().insert(GV);
}
return DSNodeHandle(NH.getNode(), NH.getOffset()+SrcNH.getOffset());
}
void ReachabilityCloner::merge(const DSNodeHandle &NH,
const DSNodeHandle &SrcNH) {
if (SrcNH.isNull()) return; // Noop
if (NH.isNull()) {
// If there is no destination node, just clone the source and assign the
// destination node to be it.
NH.mergeWith(getClonedNH(SrcNH));
return;
}
// Okay, at this point, we know that we have both a destination and a source
// node that need to be merged. Check to see if the source node has already
// been cloned.
const DSNode *SN = SrcNH.getNode();
DSNodeHandle &SCNH = NodeMap[SN]; // SourceClonedNodeHandle
Chris Lattner
committed
if (!SCNH.isNull()) { // Node already cloned?
Chris Lattner
committed
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
NH.mergeWith(DSNodeHandle(SCNH.getNode(),
SCNH.getOffset()+SrcNH.getOffset()));
return; // Nothing to do!
}
// Okay, so the source node has not already been cloned. Instead of creating
// a new DSNode, only to merge it into the one we already have, try to perform
// the merge in-place. The only case we cannot handle here is when the offset
// into the existing node is less than the offset into the virtual node we are
// merging in. In this case, we have to extend the existing node, which
// requires an allocation anyway.
DSNode *DN = NH.getNode(); // Make sure the Offset is up-to-date
if (NH.getOffset() >= SrcNH.getOffset()) {
if (!DN->isNodeCompletelyFolded()) {
// Make sure the destination node is folded if the source node is folded.
if (SN->isNodeCompletelyFolded()) {
DN->foldNodeCompletely();
DN = NH.getNode();
} else if (SN->getSize() != DN->getSize()) {
// If the two nodes are of different size, and the smaller node has the
// array bit set, collapse!
if (SN->getSize() < DN->getSize()) {
if (SN->isArray()) {
DN->foldNodeCompletely();
DN = NH.getNode();
}
} else if (DN->isArray()) {
DN->foldNodeCompletely();
DN = NH.getNode();
}
}
// Merge the type entries of the two nodes together...
if (SN->getType() != Type::VoidTy && !DN->isNodeCompletelyFolded()) {
DN->mergeTypeInfo(SN->getType(), NH.getOffset()-SrcNH.getOffset());
DN = NH.getNode();
}
}
assert(!DN->isDeadNode());
// Merge the NodeType information.
DN->mergeNodeFlags(SN->getNodeFlags() & BitsToKeep);
// Before we start merging outgoing links and updating the scalar map, make
// sure it is known that this is the representative node for the src node.
SCNH = DSNodeHandle(DN, NH.getOffset()-SrcNH.getOffset());
// If the source node contains any globals, make sure they end up in the
// scalar map with the correct offset.
if (SN->global_begin() != SN->global_end()) {
// Update the globals in the destination node itself.
DN->mergeGlobals(SN->getGlobals());
// Update the scalar map for the graph we are merging the source node
// into.
for (DSNode::global_iterator I = SN->global_begin(), E = SN->global_end();
I != E; ++I) {
GlobalValue *GV = *I;
const DSNodeHandle &SrcGNH = Src.getNodeForValue(GV);
DSNodeHandle &DestGNH = NodeMap[SrcGNH.getNode()];
assert(DestGNH.getNode()==NH.getNode() &&"Global mapping inconsistent");
Dest.getNodeForValue(GV).mergeWith(DSNodeHandle(DestGNH.getNode(),
Chris Lattner
committed
if (CloneFlags & DSGraph::UpdateInlinedGlobals)
Dest.getInlinedGlobals().insert(GV);
}
}
} else {
// We cannot handle this case without allocating a temporary node. Fall
// back on being simple.
DSNode *NewDN = new DSNode(*SN, &Dest, true /* Null out all links */);
NewDN->maskNodeTypes(BitsToKeep);
unsigned NHOffset = NH.getOffset();
NH.mergeWith(DSNodeHandle(NewDN, SrcNH.getOffset()));
Chris Lattner
committed
assert(NH.getNode() &&
(NH.getOffset() > NHOffset ||
(NH.getOffset() == 0 && NH.getNode()->isNodeCompletelyFolded())) &&
"Merging did not adjust the offset!");
// Before we start merging outgoing links and updating the scalar map, make
// sure it is known that this is the representative node for the src node.
SCNH = DSNodeHandle(NH.getNode(), NH.getOffset()-SrcNH.getOffset());
// If the source node contained any globals, make sure to create entries
// in the scalar map for them!
for (DSNode::global_iterator I = SN->global_begin(), E = SN->global_end();
I != E; ++I) {
GlobalValue *GV = *I;
const DSNodeHandle &SrcGNH = Src.getNodeForValue(GV);
DSNodeHandle &DestGNH = NodeMap[SrcGNH.getNode()];
assert(DestGNH.getNode()==NH.getNode() &&"Global mapping inconsistent");
assert(SrcGNH.getNode() == SN && "Global mapping inconsistent");
Dest.getNodeForValue(GV).mergeWith(DSNodeHandle(DestGNH.getNode(),
DestGNH.getOffset()+SrcGNH.getOffset()));
if (CloneFlags & DSGraph::UpdateInlinedGlobals)
Dest.getInlinedGlobals().insert(GV);
}
Chris Lattner
committed
}
// Next, recursively merge all outgoing links as necessary. Note that
// adding these links can cause the destination node to collapse itself at
// any time, and the current node may be merged with arbitrary other nodes.
// For this reason, we must always go through NH.
DN = 0;
for (unsigned i = 0, e = SN->getNumLinks(); i != e; ++i) {
const DSNodeHandle &SrcEdge = SN->getLink(i << DS::PointerShift);
if (!SrcEdge.isNull()) {
// Compute the offset into the current node at which to
// merge this link. In the common case, this is a linear
// relation to the offset in the original node (with
// wrapping), but if the current node gets collapsed due to
// recursive merging, we must make sure to merge in all remaining
// links at offset zero.
unsigned MergeOffset = 0;
DSNode *CN = SCNH.getNode();
if (CN->getSize() != 1)
MergeOffset = ((i << DS::PointerShift)+SCNH.getOffset()) %CN->getSize();
Chris Lattner
committed
DSNodeHandle &Link = CN->getLink(MergeOffset);
if (!Link.isNull()) {
// Perform the recursive merging. Make sure to create a temporary NH,
// because the Link can disappear in the process of recursive merging.
DSNodeHandle Tmp = Link;
merge(Tmp, SrcEdge);
} else {
merge(Link, SrcEdge);
}
Chris Lattner
committed
}
}
}
/// mergeCallSite - Merge the nodes reachable from the specified src call
/// site into the nodes reachable from DestCS.
void ReachabilityCloner::mergeCallSite(const DSCallSite &DestCS,
const DSCallSite &SrcCS) {
merge(DestCS.getRetVal(), SrcCS.getRetVal());
unsigned MinArgs = DestCS.getNumPtrArgs();
if (SrcCS.getNumPtrArgs() < MinArgs) MinArgs = SrcCS.getNumPtrArgs();
for (unsigned a = 0; a != MinArgs; ++a)
merge(DestCS.getPtrArg(a), SrcCS.getPtrArg(a));
}
//===----------------------------------------------------------------------===//
// DSCallSite Implementation
//===----------------------------------------------------------------------===//