- Use the "Fast" flag instead of "OptimizeForSize" to determine whether to emit
[oota-llvm.git] / lib / Analysis / IPA / Andersens.cpp
index 474781f6df379d73a0517c638c6716f4f6169e80..8584d06f7a7b093d98576a7b4c5c6a230f42bee4 100644 (file)
@@ -2,8 +2,8 @@
 //
 //                     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 is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
 //
 //===----------------------------------------------------------------------===//
 //
 // address taking.
 //
 // The offline constraint graph optimization portion includes offline variable
-// substitution algorithms intended to computer pointer and location
+// substitution algorithms intended to compute pointer and location
 // equivalences.  Pointer equivalences are those pointers that will have the
 // same points-to sets, and location equivalences are those variables that
-// always appear together in points-to sets.
+// always appear together in points-to sets.  It also includes an offline
+// cycle detection algorithm that allows cycles to be collapsed sooner 
+// during solving.
 //
 // The inclusion constraint solving phase iteratively propagates the inclusion
 // constraints until a fixed point is reached.  This is an O(N^3) algorithm.
@@ -48,7 +50,7 @@
 // CallReturnPos. The arguments start at getNode(F) + CallArgPos.
 //
 // Future Improvements:
-//   Offline detection of online cycles.  Use of BDD's.
+//   Use of BDD's.
 //===----------------------------------------------------------------------===//
 
 #define DEBUG_TYPE "anders-aa"
@@ -69,6 +71,7 @@
 #include <algorithm>
 #include <set>
 #include <list>
+#include <map>
 #include <stack>
 #include <vector>
 #include <queue>
@@ -86,14 +89,14 @@ STATISTIC(NumNodes      , "Number of nodes");
 STATISTIC(NumUnified    , "Number of variables unified");
 STATISTIC(NumErased     , "Number of redundant constraints erased");
 
-namespace {
-  const unsigned SelfRep = (unsigned)-1;
-  const unsigned Unvisited = (unsigned)-1;
-  // Position of the function return node relative to the function node.
-  const unsigned CallReturnPos = 1;
-  // Position of the function call node relative to the function node.
-  const unsigned CallFirstArgPos = 2;
+static const unsigned SelfRep = (unsigned)-1;
+static const unsigned Unvisited = (unsigned)-1;
+// Position of the function return node relative to the function node.
+static const unsigned CallReturnPos = 1;
+// Position of the function call node relative to the function node.
+static const unsigned CallFirstArgPos = 2;
 
+namespace {
   struct BitmapKeyInfo {
     static inline SparseBitVector<> *getEmptyKey() {
       return reinterpret_cast<SparseBitVector<> *>(-1);
@@ -139,7 +142,7 @@ namespace {
 
       Constraint(ConstraintType Ty, unsigned D, unsigned S, unsigned O = 0)
         : Type(Ty), Dest(D), Src(S), Offset(O) {
-        assert(Offset == 0 || Ty != AddressOf &&
+        assert((Offset == 0 || Ty != AddressOf) &&
                "Offset is illegal on addressof constraints");
       }
 
@@ -169,10 +172,10 @@ namespace {
     // it's thing
     struct PairKeyInfo {
       static inline std::pair<unsigned, unsigned> getEmptyKey() {
-        return std::make_pair(~0UL, ~0UL);
+        return std::make_pair(~0U, ~0U);
       }
       static inline std::pair<unsigned, unsigned> getTombstoneKey() {
-        return std::make_pair(~0UL - 1, ~0UL - 1);
+        return std::make_pair(~0U - 1, ~0U - 1);
       }
       static unsigned getHashValue(const std::pair<unsigned, unsigned> &P) {
         return P.first ^ P.second;
@@ -185,10 +188,10 @@ namespace {
     
     struct ConstraintKeyInfo {
       static inline Constraint getEmptyKey() {
-        return Constraint(Constraint::Copy, ~0UL, ~0UL, ~0UL);
+        return Constraint(Constraint::Copy, ~0U, ~0U, ~0U);
       }
       static inline Constraint getTombstoneKey() {
-        return Constraint(Constraint::Copy, ~0UL - 1, ~0UL - 1, ~0UL - 1);
+        return Constraint(Constraint::Copy, ~0U - 1, ~0U - 1, ~0U - 1);
       }
       static unsigned getHashValue(const Constraint &C) {
         return C.Src ^ C.Dest ^ C.Type ^ C.Offset;
@@ -284,7 +287,7 @@ namespace {
         Timestamp = Counter++;
       }
 
-      bool isRep() {
+      bool isRep() const {
         return( (int) NodeRep < 0 );
       }
     };
@@ -418,10 +421,17 @@ namespace {
     // pointer equivalent but not location equivalent variables. -1 if we have
     // no representative node for this pointer equivalence class yet.
     std::vector<int> PENLEClass2Node;
+    // Union/Find for HCD
+    std::vector<unsigned> HCDSCCRep;
+    // HCD's offline-detected cycles; "Statically DeTected"
+    // -1 if not part of such a cycle, otherwise a representative node.
+    std::vector<int> SDT;
+    // Whether to use SDT (UniteNodes can use it during solving, but not before)
+    bool SDTActive;
 
   public:
     static char ID;
-    Andersens() : ModulePass((intptr_t)&ID) {}
+    Andersens() : ModulePass(&ID) {}
 
     bool runOnModule(Module &M) {
       InitializeAliasAnalysis(this);
@@ -437,10 +447,11 @@ namespace {
 
       // Free the constraints list, as we don't need it to respond to alias
       // requests.
-      ObjectNodes.clear();
-      ReturnNodes.clear();
-      VarargNodes.clear();
       std::vector<Constraint>().swap(Constraints);
+      //These are needed for Print() (-analyze in opt)
+      //ObjectNodes.clear();
+      //ReturnNodes.clear();
+      //VarargNodes.clear();
       return false;
     }
 
@@ -501,7 +512,7 @@ namespace {
 
     /// getObject - Return the node corresponding to the memory object for the
     /// specified global or allocation instruction.
-    unsigned getObject(Value *V) {
+    unsigned getObject(Value *V) const {
       DenseMap<Value*, unsigned>::iterator I = ObjectNodes.find(V);
       assert(I != ObjectNodes.end() &&
              "Value does not have an object in the points-to graph!");
@@ -510,7 +521,7 @@ namespace {
 
     /// getReturnNode - Return the node representing the return value for the
     /// specified function.
-    unsigned getReturnNode(Function *F) {
+    unsigned getReturnNode(Function *F) const {
       DenseMap<Function*, unsigned>::iterator I = ReturnNodes.find(F);
       assert(I != ReturnNodes.end() && "Function does not return a value!");
       return I->second;
@@ -518,7 +529,7 @@ namespace {
 
     /// getVarargNode - Return the node representing the variable arguments
     /// formal for the specified function.
-    unsigned getVarargNode(Function *F) {
+    unsigned getVarargNode(Function *F) const {
       DenseMap<Function*, unsigned>::iterator I = VarargNodes.find(F);
       assert(I != VarargNodes.end() && "Function does not take var args!");
       return I->second;
@@ -535,6 +546,7 @@ namespace {
     unsigned UniteNodes(unsigned First, unsigned Second,
                         bool UnionByRank = true);
     unsigned FindNode(unsigned Node);
+    unsigned FindNode(unsigned Node) const;
 
     void IdentifyObjects(Module &M);
     void CollectConstraints(Module &M);
@@ -546,6 +558,8 @@ namespace {
     void RewriteConstraints();
     void HU();
     void HVN();
+    void HCD();
+    void Search(unsigned Node);
     void UnitePointerEquivalences();
     void SolveConstraints();
     bool QueryNode(unsigned Node);
@@ -561,11 +575,11 @@ namespace {
     bool AddConstraintsForExternalCall(CallSite CS, Function *F);
 
 
-    void PrintNode(Node *N);
-    void PrintConstraints();
-    void PrintConstraint(const Constraint &);
-    void PrintLabels();
-    void PrintPointsToGraph();
+    void PrintNode(const Node *N) const;
+    void PrintConstraints() const ;
+    void PrintConstraint(const Constraint &) const;
+    void PrintLabels() const;
+    void PrintPointsToGraph() const;
 
     //===------------------------------------------------------------------===//
     // Instruction visitation methods for adding constraints
@@ -587,16 +601,22 @@ namespace {
     void visitVAArg(VAArgInst &I);
     void visitInstruction(Instruction &I);
 
+    //===------------------------------------------------------------------===//
+    // Implement Analyize interface
+    //
+    void print(std::ostream &O, const Module* M) const {
+      PrintPointsToGraph();
+    }
   };
+}
 
-  char Andersens::ID = 0;
-  RegisterPass<Andersens> X("anders-aa",
-                            "Andersen's Interprocedural Alias Analysis");
-  RegisterAnalysisGroup<AliasAnalysis> Y(X);
+char Andersens::ID = 0;
+static RegisterPass<Andersens>
+X("anders-aa", "Andersen's Interprocedural Alias Analysis", false, true);
+static RegisterAnalysisGroup<AliasAnalysis> Y(X);
 
-  // Initialize Timestamp Counter (static).
-  unsigned Andersens::Node::Counter = 0;
-}
+// Initialize Timestamp Counter (static).
+unsigned Andersens::Node::Counter = 0;
 
 ModulePass *llvm::createAndersensPass() { return new Andersens(); }
 
@@ -633,9 +653,13 @@ Andersens::getModRefInfo(CallSite CS, Value *P, unsigned Size) {
 
       if (N1->PointsTo->empty())
         return NoModRef;
-
+#if FULL_UNIVERSAL
+      if (!UniversalSet->PointsTo->test(FindNode(getNode(P))))
+        return NoModRef;  // Universal set does not contain P
+#else
       if (!N1->PointsTo->test(UniversalSet))
         return NoModRef;  // P doesn't point to the universal set.
+#endif
     }
 
   return AliasAnalysis::getModRefInfo(CS, P, Size);
@@ -678,7 +702,7 @@ void Andersens::getMustAliases(Value *P, std::vector<Value*> &RetVals) {
 /// return true.
 ///
 bool Andersens::pointsToConstantMemory(const Value *P) {
-  Node *N = &GraphNodes[FindNode(getNode((Value*)P))];
+  Node *N = &GraphNodes[FindNode(getNode(const_cast<Value*>(P)))];
   unsigned i;
 
   for (SparseBitVector<>::iterator bi = N->PointsTo->begin();
@@ -839,7 +863,7 @@ unsigned Andersens::getNodeForConstantPointerTarget(Constant *C) {
 /// object N, which contains values indicated by C.
 void Andersens::AddGlobalInitializerConstraints(unsigned NodeIndex,
                                                 Constant *C) {
-  if (C->getType()->isFirstClassType()) {
+  if (C->getType()->isSingleValueType()) {
     if (isa<PointerType>(C->getType()))
       Constraints.push_back(Constraint(Constraint::Copy, NodeIndex,
                                        getNodeForConstantPointer(C)));
@@ -879,8 +903,7 @@ bool Andersens::AddConstraintsForExternalCall(CallSite CS, Function *F) {
       F->getName() == "atol" || F->getName() == "atoll" ||
       F->getName() == "remove" || F->getName() == "unlink" ||
       F->getName() == "rename" || F->getName() == "memcmp" ||
-      F->getName() == "llvm.memset.i32" ||
-      F->getName() == "llvm.memset.i64" ||
+      F->getName() == "llvm.memset" ||
       F->getName() == "strcmp" || F->getName() == "strncmp" ||
       F->getName() == "execl" || F->getName() == "execlp" ||
       F->getName() == "execle" || F->getName() == "execv" ||
@@ -918,31 +941,44 @@ bool Andersens::AddConstraintsForExternalCall(CallSite CS, Function *F) {
 
 
   // These functions do induce points-to edges.
-  if (F->getName() == "llvm.memcpy.i32" || F->getName() == "llvm.memcpy.i64" ||
-      F->getName() == "llvm.memmove.i32" ||F->getName() == "llvm.memmove.i64" ||
+  if (F->getName() == "llvm.memcpy" ||
+      F->getName() == "llvm.memmove" ||
       F->getName() == "memmove") {
 
-    // *Dest = *Src, which requires an artificial graph node to represent the
-    // constraint.  It is broken up into *Dest = temp, temp = *Src
-    unsigned FirstArg = getNode(CS.getArgument(0));
-    unsigned SecondArg = getNode(CS.getArgument(1));
-    unsigned TempArg = GraphNodes.size();
-    GraphNodes.push_back(Node());
-    Constraints.push_back(Constraint(Constraint::Store,
-                                     FirstArg, TempArg));
-    Constraints.push_back(Constraint(Constraint::Load,
-                                     TempArg, SecondArg));
-    return true;
+    const FunctionType *FTy = F->getFunctionType();
+    if (FTy->getNumParams() > 1 && 
+        isa<PointerType>(FTy->getParamType(0)) &&
+        isa<PointerType>(FTy->getParamType(1))) {
+
+      // *Dest = *Src, which requires an artificial graph node to represent the
+      // constraint.  It is broken up into *Dest = temp, temp = *Src
+      unsigned FirstArg = getNode(CS.getArgument(0));
+      unsigned SecondArg = getNode(CS.getArgument(1));
+      unsigned TempArg = GraphNodes.size();
+      GraphNodes.push_back(Node());
+      Constraints.push_back(Constraint(Constraint::Store,
+                                       FirstArg, TempArg));
+      Constraints.push_back(Constraint(Constraint::Load,
+                                       TempArg, SecondArg));
+      // In addition, Dest = Src
+      Constraints.push_back(Constraint(Constraint::Copy,
+                                       FirstArg, SecondArg));
+      return true;
+    }
   }
 
   // Result = Arg0
   if (F->getName() == "realloc" || F->getName() == "strchr" ||
       F->getName() == "strrchr" || F->getName() == "strstr" ||
       F->getName() == "strtok") {
-    Constraints.push_back(Constraint(Constraint::Copy,
-                                     getNode(CS.getInstruction()),
-                                     getNode(CS.getArgument(0))));
-    return true;
+    const FunctionType *FTy = F->getFunctionType();
+    if (FTy->getNumParams() > 0 && 
+        isa<PointerType>(FTy->getParamType(0))) {
+      Constraints.push_back(Constraint(Constraint::Copy,
+                                       getNode(CS.getInstruction()),
+                                       getNode(CS.getArgument(0))));
+      return true;
+    }
   }
 
   return false;
@@ -1048,7 +1084,7 @@ void Andersens::CollectConstraints(Module &M) {
     // At some point we should just add constraints for the escaping functions
     // at solve time, but this slows down solving. For now, we simply mark
     // address taken functions as escaping and treat them as external.
-    if (!F->hasInternalLinkage() || AnalyzeUsesOfFunction(F))
+    if (!F->hasLocalLinkage() || AnalyzeUsesOfFunction(F))
       AddConstraintsForNonInternalLinkage(F);
 
     if (!F->isDeclaration()) {
@@ -1252,29 +1288,43 @@ void Andersens::AddConstraintsForCall(CallSite CS, Function *F) {
   }
 
   CallSite::arg_iterator ArgI = CS.arg_begin(), ArgE = CS.arg_end();
+  bool external = !F ||  F->isDeclaration();
   if (F) {
     // Direct Call
     Function::arg_iterator AI = F->arg_begin(), AE = F->arg_end();
-    for (; AI != AE && ArgI != ArgE; ++AI, ++ArgI)
-      if (isa<PointerType>(AI->getType())) {
-        if (isa<PointerType>((*ArgI)->getType())) {
-          // Copy the actual argument into the formal argument.
-          Constraints.push_back(Constraint(Constraint::Copy, getNode(AI),
-                                           getNode(*ArgI)));
-        } else {
-          Constraints.push_back(Constraint(Constraint::Copy, getNode(AI),
-                                           UniversalSet));
-        }
-      } else if (isa<PointerType>((*ArgI)->getType())) {
+    for (; AI != AE && ArgI != ArgE; ++AI, ++ArgI) 
+      {
+#if !FULL_UNIVERSAL
+        if (external && isa<PointerType>((*ArgI)->getType())) 
+          {
+            // Add constraint that ArgI can now point to anything due to
+            // escaping, as can everything it points to. The second portion of
+            // this should be taken care of by universal = *universal
+            Constraints.push_back(Constraint(Constraint::Copy,
+                                             getNode(*ArgI),
+                                             UniversalSet));
+          }
+#endif
+        if (isa<PointerType>(AI->getType())) {
+          if (isa<PointerType>((*ArgI)->getType())) {
+            // Copy the actual argument into the formal argument.
+            Constraints.push_back(Constraint(Constraint::Copy, getNode(AI),
+                                             getNode(*ArgI)));
+          } else {
+            Constraints.push_back(Constraint(Constraint::Copy, getNode(AI),
+                                             UniversalSet));
+          }
+        } else if (isa<PointerType>((*ArgI)->getType())) {
 #if FULL_UNIVERSAL
-        Constraints.push_back(Constraint(Constraint::Copy,
-                                         UniversalSet,
-                                         getNode(*ArgI)));
+          Constraints.push_back(Constraint(Constraint::Copy,
+                                           UniversalSet,
+                                           getNode(*ArgI)));
 #else
-        Constraints.push_back(Constraint(Constraint::Copy,
-                                         getNode(*ArgI),
-                                         UniversalSet));
+          Constraints.push_back(Constraint(Constraint::Copy,
+                                           getNode(*ArgI),
+                                           UniversalSet));
 #endif
+        }
       }
   } else {
     //Indirect Call
@@ -1931,7 +1981,7 @@ void Andersens::RewriteConstraints() {
 
     C.Src = FindEquivalentNode(RHSNode, RHSLabel);
     C.Dest = FindEquivalentNode(FindNode(LHSNode), LHSLabel);
-    if (C.Src == C.Dest && C.Type == Constraint::Copy
+    if ((C.Src == C.Dest && C.Type == Constraint::Copy)
         || Seen.count(C))
       continue;
 
@@ -1964,7 +2014,7 @@ unsigned Andersens::FindEquivalentNode(unsigned NodeIndex,
   return NodeIndex;
 }
 
-void Andersens::PrintLabels() {
+void Andersens::PrintLabels() const {
   for (unsigned i = 0; i < GraphNodes.size(); ++i) {
     if (i < FirstRefNode) {
       PrintNode(&GraphNodes[i]);
@@ -1985,11 +2035,141 @@ void Andersens::PrintLabels() {
   }
 }
 
+/// The technique used here is described in "The Ant and the
+/// Grasshopper: Fast and Accurate Pointer Analysis for Millions of
+/// Lines of Code. In Programming Language Design and Implementation
+/// (PLDI), June 2007." It is known as the "HCD" (Hybrid Cycle
+/// Detection) algorithm. It is called a hybrid because it performs an
+/// offline analysis and uses its results during the solving (online)
+/// phase. This is just the offline portion; the results of this
+/// operation are stored in SDT and are later used in SolveContraints()
+/// and UniteNodes().
+void Andersens::HCD() {
+  DOUT << "Starting HCD.\n";
+  HCDSCCRep.resize(GraphNodes.size());
+
+  for (unsigned i = 0; i < GraphNodes.size(); ++i) {
+    GraphNodes[i].Edges = new SparseBitVector<>;
+    HCDSCCRep[i] = i;
+  }
+
+  for (unsigned i = 0, e = Constraints.size(); i != e; ++i) {
+    Constraint &C = Constraints[i];
+    assert (C.Src < GraphNodes.size() && C.Dest < GraphNodes.size());
+    if (C.Type == Constraint::AddressOf) {
+      continue;
+    } else if (C.Type == Constraint::Load) {
+      if( C.Offset == 0 )
+        GraphNodes[C.Dest].Edges->set(C.Src + FirstRefNode);
+    } else if (C.Type == Constraint::Store) {
+      if( C.Offset == 0 )
+        GraphNodes[C.Dest + FirstRefNode].Edges->set(C.Src);
+    } else {
+      GraphNodes[C.Dest].Edges->set(C.Src);
+    }
+  }
+
+  Node2DFS.insert(Node2DFS.begin(), GraphNodes.size(), 0);
+  Node2Deleted.insert(Node2Deleted.begin(), GraphNodes.size(), false);
+  Node2Visited.insert(Node2Visited.begin(), GraphNodes.size(), false);
+  SDT.insert(SDT.begin(), GraphNodes.size() / 2, -1);
+
+  DFSNumber = 0;
+  for (unsigned i = 0; i < GraphNodes.size(); ++i) {
+    unsigned Node = HCDSCCRep[i];
+    if (!Node2Deleted[Node])
+      Search(Node);
+  }
+
+  for (unsigned i = 0; i < GraphNodes.size(); ++i)
+    if (GraphNodes[i].Edges != NULL) {
+      delete GraphNodes[i].Edges;
+      GraphNodes[i].Edges = NULL;
+    }
+
+  while( !SCCStack.empty() )
+    SCCStack.pop();
+
+  Node2DFS.clear();
+  Node2Visited.clear();
+  Node2Deleted.clear();
+  HCDSCCRep.clear();
+  DOUT << "HCD complete.\n";
+}
+
+// Component of HCD: 
+// Use Nuutila's variant of Tarjan's algorithm to detect
+// Strongly-Connected Components (SCCs). For non-trivial SCCs
+// containing ref nodes, insert the appropriate information in SDT.
+void Andersens::Search(unsigned Node) {
+  unsigned MyDFS = DFSNumber++;
+
+  Node2Visited[Node] = true;
+  Node2DFS[Node] = MyDFS;
+
+  for (SparseBitVector<>::iterator Iter = GraphNodes[Node].Edges->begin(),
+                                   End  = GraphNodes[Node].Edges->end();
+       Iter != End;
+       ++Iter) {
+    unsigned J = HCDSCCRep[*Iter];
+    assert(GraphNodes[J].isRep() && "Debug check; must be representative");
+    if (!Node2Deleted[J]) {
+      if (!Node2Visited[J])
+        Search(J);
+      if (Node2DFS[Node] > Node2DFS[J])
+        Node2DFS[Node] = Node2DFS[J];
+    }
+  }
+
+  if( MyDFS != Node2DFS[Node] ) {
+    SCCStack.push(Node);
+    return;
+  }
+
+  // This node is the root of a SCC, so process it.
+  //
+  // If the SCC is "non-trivial" (not a singleton) and contains a reference 
+  // node, we place this SCC into SDT.  We unite the nodes in any case.
+  if (!SCCStack.empty() && Node2DFS[SCCStack.top()] >= MyDFS) {
+    SparseBitVector<> SCC;
+
+    SCC.set(Node);
+
+    bool Ref = (Node >= FirstRefNode);
+
+    Node2Deleted[Node] = true;
+
+    do {
+      unsigned P = SCCStack.top(); SCCStack.pop();
+      Ref |= (P >= FirstRefNode);
+      SCC.set(P);
+      HCDSCCRep[P] = Node;
+    } while (!SCCStack.empty() && Node2DFS[SCCStack.top()] >= MyDFS);
+
+    if (Ref) {
+      unsigned Rep = SCC.find_first();
+      assert(Rep < FirstRefNode && "The SCC didn't have a non-Ref node!");
+
+      SparseBitVector<>::iterator i = SCC.begin();
+
+      // Skip over the non-ref nodes
+      while( *i < FirstRefNode )
+        ++i;
+
+      while( i != SCC.end() )
+        SDT[ (*i++) - FirstRefNode ] = Rep;
+    }
+  }
+}
+
+
 /// Optimize the constraints by performing offline variable substitution and
 /// other optimizations.
 void Andersens::OptimizeConstraints() {
   DOUT << "Beginning constraint optimization\n";
 
+  SDTActive = false;
+
   // Function related nodes need to stay in the same relative position and can't
   // be location equivalent.
   for (std::map<unsigned, unsigned>::iterator Iter = MaxK.begin();
@@ -2051,12 +2231,25 @@ void Andersens::OptimizeConstraints() {
     if (FindNode(i) == i) {
       Node *N = &GraphNodes[i];
       delete N->PointsTo;
+      N->PointsTo = NULL;
       delete N->PredEdges;
+      N->PredEdges = NULL;
       delete N->ImplicitPredEdges;
+      N->ImplicitPredEdges = NULL;
       delete N->PointedToBy;
+      N->PointedToBy = NULL;
     }
   }
+
+  // perform Hybrid Cycle Detection (HCD)
+  HCD();
+  SDTActive = true;
+
+  // No longer any need for the upper half of GraphNodes (for ref nodes).
   GraphNodes.erase(GraphNodes.begin() + FirstRefNode, GraphNodes.end());
+
+  // HCD complete.
+
   DOUT << "Finished constraint optimization\n";
   FirstRefNode = 0;
   FirstAdrNode = 0;
@@ -2221,6 +2414,14 @@ void Andersens::SolveConstraints() {
     }
   }
   std::queue<unsigned int> TarjanWL;
+#if !FULL_UNIVERSAL
+  // "Rep and special variables" - in order for HCD to maintain conservative
+  // results when !FULL_UNIVERSAL, we need to treat the special variables in
+  // the same way that the !FULL_UNIVERSAL tweak does throughout the rest of
+  // the analysis - it's ok to add edges from the special nodes, but never
+  // *to* the special nodes.
+  std::vector<unsigned int> RSV;
+#endif
   while( !CurrWL->empty() ) {
     DOUT << "Starting iteration #" << ++NumIters << "\n";
 
@@ -2259,6 +2460,39 @@ void Andersens::SolveConstraints() {
         continue;
 
       *(CurrNode->OldPointsTo) |= CurrPointsTo;
+
+      // Check the offline-computed equivalencies from HCD.
+      bool SCC = false;
+      unsigned Rep;
+
+      if (SDT[CurrNodeIndex] >= 0) {
+        SCC = true;
+        Rep = FindNode(SDT[CurrNodeIndex]);
+
+#if !FULL_UNIVERSAL
+        RSV.clear();
+#endif
+        for (SparseBitVector<>::iterator bi = CurrPointsTo.begin();
+             bi != CurrPointsTo.end(); ++bi) {
+          unsigned Node = FindNode(*bi);
+#if !FULL_UNIVERSAL
+          if (Node < NumberSpecialNodes) {
+            RSV.push_back(Node);
+            continue;
+          }
+#endif
+          Rep = UniteNodes(Rep,Node);
+        }
+#if !FULL_UNIVERSAL
+        RSV.push_back(Rep);
+#endif
+
+        NextWL->insert(&GraphNodes[Rep]);
+
+        if ( ! CurrNode->isRep() )
+          continue;
+      }
+
       Seen.clear();
 
       /* Now process the constraints for this node.  */
@@ -2301,39 +2535,74 @@ void Andersens::SolveConstraints() {
           li++;
           continue;
         }
-        // TODO: hybrid cycle detection would go here, we should check
-        // if it was a statically detected offline equivalence that
-        // involves pointers , and if so, remove the redundant constraints.
 
-        const SparseBitVector<> &Solution = CurrPointsTo;
-
-        for (SparseBitVector<>::iterator bi = Solution.begin();
-             bi != Solution.end();
-             ++bi) {
-          CurrMember = *bi;
-
-          // Need to increment the member by K since that is where we are
-          // supposed to copy to/from.  Note that in positive weight cycles,
-          // which occur in address taking of fields, K can go past
-          // MaxK[CurrMember] elements, even though that is all it could point
-          // to.
-          if (K > 0 && K > MaxK[CurrMember])
-            continue;
-          else
-            CurrMember = FindNode(CurrMember + K);
+        // See if we can use Hybrid Cycle Detection (that is, check
+        // if it was a statically detected offline equivalence that
+        // involves pointers; if so, remove the redundant constraints).
+        if( SCC && K == 0 ) {
+#if FULL_UNIVERSAL
+          CurrMember = Rep;
 
-          // Add an edge to the graph, so we can just do regular bitmap ior next
-          // time.  It may also let us notice a cycle.
-#if !FULL_UNIVERSAL
-          if (*Dest < NumberSpecialNodes)
-            continue;
-#endif
           if (GraphNodes[*Src].Edges->test_and_set(*Dest))
             if (GraphNodes[*Dest].PointsTo |= *(GraphNodes[*Src].PointsTo))
               NextWL->insert(&GraphNodes[*Dest]);
+#else
+          for (unsigned i=0; i < RSV.size(); ++i) {
+            CurrMember = RSV[i];
+
+            if (*Dest < NumberSpecialNodes)
+              continue;
+            if (GraphNodes[*Src].Edges->test_and_set(*Dest))
+              if (GraphNodes[*Dest].PointsTo |= *(GraphNodes[*Src].PointsTo))
+                NextWL->insert(&GraphNodes[*Dest]);
+          }
+#endif
+          // since all future elements of the points-to set will be
+          // equivalent to the current ones, the complex constraints
+          // become redundant.
+          //
+          std::list<Constraint>::iterator lk = li; li++;
+#if !FULL_UNIVERSAL
+          // In this case, we can still erase the constraints when the
+          // elements of the points-to sets are referenced by *Dest,
+          // but not when they are referenced by *Src (i.e. for a Load
+          // constraint). This is because if another special variable is
+          // put into the points-to set later, we still need to add the
+          // new edge from that special variable.
+          if( lk->Type != Constraint::Load)
+#endif
+          GraphNodes[CurrNodeIndex].Constraints.erase(lk);
+        } else {
+          const SparseBitVector<> &Solution = CurrPointsTo;
+
+          for (SparseBitVector<>::iterator bi = Solution.begin();
+               bi != Solution.end();
+               ++bi) {
+            CurrMember = *bi;
+
+            // Need to increment the member by K since that is where we are
+            // supposed to copy to/from.  Note that in positive weight cycles,
+            // which occur in address taking of fields, K can go past
+            // MaxK[CurrMember] elements, even though that is all it could point
+            // to.
+            if (K > 0 && K > MaxK[CurrMember])
+              continue;
+            else
+              CurrMember = FindNode(CurrMember + K);
+
+            // Add an edge to the graph, so we can just do regular
+            // bitmap ior next time.  It may also let us notice a cycle.
+#if !FULL_UNIVERSAL
+            if (*Dest < NumberSpecialNodes)
+              continue;
+#endif
+            if (GraphNodes[*Src].Edges->test_and_set(*Dest))
+              if (GraphNodes[*Dest].PointsTo |= *(GraphNodes[*Src].PointsTo))
+                NextWL->insert(&GraphNodes[*Dest]);
 
+          }
+          li++;
         }
-        li++;
       }
       SparseBitVector<> NewEdges;
       SparseBitVector<> ToErase;
@@ -2347,15 +2616,15 @@ void Andersens::SolveConstraints() {
         unsigned DestVar = *bi;
         unsigned Rep = FindNode(DestVar);
 
-       // If we ended up with this node as our destination, or we've already
-       // got an edge for the representative, delete the current edge.
-       if (Rep == CurrNodeIndex ||
-           (Rep != DestVar && NewEdges.test(Rep))) {
-           ToErase.set(DestVar);
-           continue;
-       }
+        // If we ended up with this node as our destination, or we've already
+        // got an edge for the representative, delete the current edge.
+        if (Rep == CurrNodeIndex ||
+            (Rep != DestVar && NewEdges.test(Rep))) {
+            ToErase.set(DestVar);
+            continue;
+        }
         
-       std::pair<unsigned,unsigned> edge(CurrNodeIndex,Rep);
+        std::pair<unsigned,unsigned> edge(CurrNodeIndex,Rep);
         
         // This is where we do lazy cycle detection.
         // If this is a cycle candidate (equal points-to sets and this
@@ -2395,6 +2664,8 @@ void Andersens::SolveConstraints() {
     delete N->OldPointsTo;
     delete N->Edges;
   }
+  SDTActive = false;
+  SDT.clear();
 }
 
 //===----------------------------------------------------------------------===//
@@ -2461,7 +2732,16 @@ unsigned Andersens::UniteNodes(unsigned First, unsigned Second,
   DEBUG(PrintNode(SecondNode));
   DOUT << "\n";
 
-  // TODO: Handle SDT
+  if (SDTActive)
+    if (SDT[Second] >= 0) {
+      if (SDT[First] < 0)
+        SDT[First] = SDT[Second];
+      else {
+        UniteNodes( FindNode(SDT[First]), FindNode(SDT[Second]) );
+        First = FindNode(First);
+      }
+    }
+
   return First;
 }
 
@@ -2477,11 +2757,23 @@ unsigned Andersens::FindNode(unsigned NodeIndex) {
     return (N->NodeRep = FindNode(N->NodeRep));
 }
 
+// Find the index into GraphNodes of the node representing Node, 
+// don't perform path compression along the way (for Print)
+unsigned Andersens::FindNode(unsigned NodeIndex) const {
+  assert (NodeIndex < GraphNodes.size()
+          && "Attempting to find a node that can't exist");
+  const Node *N = &GraphNodes[NodeIndex];
+  if (N->isRep())
+    return NodeIndex;
+  else
+    return FindNode(N->NodeRep);
+}
+
 //===----------------------------------------------------------------------===//
 //                               Debugging Output
 //===----------------------------------------------------------------------===//
 
-void Andersens::PrintNode(Node *N) {
+void Andersens::PrintNode(const Node *N) const {
   if (N == &GraphNodes[UniversalSet]) {
     cerr << "<universal>";
     return;
@@ -2525,7 +2817,7 @@ void Andersens::PrintNode(Node *N) {
     if (N == &GraphNodes[getObject(V)])
       cerr << "<mem>";
 }
-void Andersens::PrintConstraint(const Constraint &C) {
+void Andersens::PrintConstraint(const Constraint &C) const {
   if (C.Type == Constraint::Store) {
     cerr << "*";
     if (C.Offset != 0)
@@ -2550,18 +2842,18 @@ void Andersens::PrintConstraint(const Constraint &C) {
   cerr << "\n";
 }
 
-void Andersens::PrintConstraints() {
+void Andersens::PrintConstraints() const {
   cerr << "Constraints:\n";
 
   for (unsigned i = 0, e = Constraints.size(); i != e; ++i)
     PrintConstraint(Constraints[i]);
 }
 
-void Andersens::PrintPointsToGraph() {
+void Andersens::PrintPointsToGraph() const {
   cerr << "Points-to graph:\n";
   for (unsigned i = 0, e = GraphNodes.size(); i != e; ++i) {
-    Node *N = &GraphNodes[i];
-    if (FindNode (i) != i) {
+    const Node *N = &GraphNodes[i];
+    if (FindNode(i) != i) {
       PrintNode(N);
       cerr << "\t--> same as ";
       PrintNode(&GraphNodes[FindNode(i)]);