Home | History | Annotate | Download | only in Support
      1 //===-- Support/FoldingSet.cpp - Uniquing Hash Set --------------*- C++ -*-===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 // This file implements a hash set that can be used to remove duplication of
     11 // nodes in a graph.  This code was originally created by Chris Lattner for use
     12 // with SelectionDAGCSEMap, but was isolated to provide use across the llvm code
     13 // set.
     14 //
     15 //===----------------------------------------------------------------------===//
     16 
     17 #include "llvm/ADT/FoldingSet.h"
     18 #include "llvm/ADT/Hashing.h"
     19 #include "llvm/Support/Allocator.h"
     20 #include "llvm/Support/ErrorHandling.h"
     21 #include "llvm/Support/MathExtras.h"
     22 #include "llvm/Support/Host.h"
     23 #include <cassert>
     24 #include <cstring>
     25 using namespace llvm;
     26 
     27 //===----------------------------------------------------------------------===//
     28 // FoldingSetNodeIDRef Implementation
     29 
     30 /// ComputeHash - Compute a strong hash value for this FoldingSetNodeIDRef,
     31 /// used to lookup the node in the FoldingSetImpl.
     32 unsigned FoldingSetNodeIDRef::ComputeHash() const {
     33   return static_cast<unsigned>(hash_combine_range(Data, Data+Size));
     34 }
     35 
     36 bool FoldingSetNodeIDRef::operator==(FoldingSetNodeIDRef RHS) const {
     37   if (Size != RHS.Size) return false;
     38   return memcmp(Data, RHS.Data, Size*sizeof(*Data)) == 0;
     39 }
     40 
     41 /// Used to compare the "ordering" of two nodes as defined by the
     42 /// profiled bits and their ordering defined by memcmp().
     43 bool FoldingSetNodeIDRef::operator<(FoldingSetNodeIDRef RHS) const {
     44   if (Size != RHS.Size)
     45     return Size < RHS.Size;
     46   return memcmp(Data, RHS.Data, Size*sizeof(*Data)) < 0;
     47 }
     48 
     49 //===----------------------------------------------------------------------===//
     50 // FoldingSetNodeID Implementation
     51 
     52 /// Add* - Add various data types to Bit data.
     53 ///
     54 void FoldingSetNodeID::AddPointer(const void *Ptr) {
     55   // Note: this adds pointers to the hash using sizes and endianness that
     56   // depend on the host.  It doesn't matter however, because hashing on
     57   // pointer values in inherently unstable.  Nothing  should depend on the
     58   // ordering of nodes in the folding set.
     59   Bits.append(reinterpret_cast<unsigned *>(&Ptr),
     60               reinterpret_cast<unsigned *>(&Ptr+1));
     61 }
     62 void FoldingSetNodeID::AddInteger(signed I) {
     63   Bits.push_back(I);
     64 }
     65 void FoldingSetNodeID::AddInteger(unsigned I) {
     66   Bits.push_back(I);
     67 }
     68 void FoldingSetNodeID::AddInteger(long I) {
     69   AddInteger((unsigned long)I);
     70 }
     71 void FoldingSetNodeID::AddInteger(unsigned long I) {
     72   if (sizeof(long) == sizeof(int))
     73     AddInteger(unsigned(I));
     74   else if (sizeof(long) == sizeof(long long)) {
     75     AddInteger((unsigned long long)I);
     76   } else {
     77     llvm_unreachable("unexpected sizeof(long)");
     78   }
     79 }
     80 void FoldingSetNodeID::AddInteger(long long I) {
     81   AddInteger((unsigned long long)I);
     82 }
     83 void FoldingSetNodeID::AddInteger(unsigned long long I) {
     84   AddInteger(unsigned(I));
     85   if ((uint64_t)(unsigned)I != I)
     86     Bits.push_back(unsigned(I >> 32));
     87 }
     88 
     89 void FoldingSetNodeID::AddString(StringRef String) {
     90   unsigned Size =  String.size();
     91   Bits.push_back(Size);
     92   if (!Size) return;
     93 
     94   unsigned Units = Size / 4;
     95   unsigned Pos = 0;
     96   const unsigned *Base = (const unsigned*) String.data();
     97 
     98   // If the string is aligned do a bulk transfer.
     99   if (!((intptr_t)Base & 3)) {
    100     Bits.append(Base, Base + Units);
    101     Pos = (Units + 1) * 4;
    102   } else {
    103     // Otherwise do it the hard way.
    104     // To be compatible with above bulk transfer, we need to take endianness
    105     // into account.
    106     if (sys::isBigEndianHost()) {
    107       for (Pos += 4; Pos <= Size; Pos += 4) {
    108         unsigned V = ((unsigned char)String[Pos - 4] << 24) |
    109                      ((unsigned char)String[Pos - 3] << 16) |
    110                      ((unsigned char)String[Pos - 2] << 8) |
    111                       (unsigned char)String[Pos - 1];
    112         Bits.push_back(V);
    113       }
    114     } else {
    115       assert(sys::isLittleEndianHost() && "Unexpected host endianness");
    116       for (Pos += 4; Pos <= Size; Pos += 4) {
    117         unsigned V = ((unsigned char)String[Pos - 1] << 24) |
    118                      ((unsigned char)String[Pos - 2] << 16) |
    119                      ((unsigned char)String[Pos - 3] << 8) |
    120                       (unsigned char)String[Pos - 4];
    121         Bits.push_back(V);
    122       }
    123     }
    124   }
    125 
    126   // With the leftover bits.
    127   unsigned V = 0;
    128   // Pos will have overshot size by 4 - #bytes left over.
    129   // No need to take endianness into account here - this is always executed.
    130   switch (Pos - Size) {
    131   case 1: V = (V << 8) | (unsigned char)String[Size - 3]; // Fall thru.
    132   case 2: V = (V << 8) | (unsigned char)String[Size - 2]; // Fall thru.
    133   case 3: V = (V << 8) | (unsigned char)String[Size - 1]; break;
    134   default: return; // Nothing left.
    135   }
    136 
    137   Bits.push_back(V);
    138 }
    139 
    140 // AddNodeID - Adds the Bit data of another ID to *this.
    141 void FoldingSetNodeID::AddNodeID(const FoldingSetNodeID &ID) {
    142   Bits.append(ID.Bits.begin(), ID.Bits.end());
    143 }
    144 
    145 /// ComputeHash - Compute a strong hash value for this FoldingSetNodeID, used to
    146 /// lookup the node in the FoldingSetImpl.
    147 unsigned FoldingSetNodeID::ComputeHash() const {
    148   return FoldingSetNodeIDRef(Bits.data(), Bits.size()).ComputeHash();
    149 }
    150 
    151 /// operator== - Used to compare two nodes to each other.
    152 ///
    153 bool FoldingSetNodeID::operator==(const FoldingSetNodeID &RHS)const{
    154   return *this == FoldingSetNodeIDRef(RHS.Bits.data(), RHS.Bits.size());
    155 }
    156 
    157 /// operator== - Used to compare two nodes to each other.
    158 ///
    159 bool FoldingSetNodeID::operator==(FoldingSetNodeIDRef RHS) const {
    160   return FoldingSetNodeIDRef(Bits.data(), Bits.size()) == RHS;
    161 }
    162 
    163 /// Used to compare the "ordering" of two nodes as defined by the
    164 /// profiled bits and their ordering defined by memcmp().
    165 bool FoldingSetNodeID::operator<(const FoldingSetNodeID &RHS)const{
    166   return *this < FoldingSetNodeIDRef(RHS.Bits.data(), RHS.Bits.size());
    167 }
    168 
    169 bool FoldingSetNodeID::operator<(FoldingSetNodeIDRef RHS) const {
    170   return FoldingSetNodeIDRef(Bits.data(), Bits.size()) < RHS;
    171 }
    172 
    173 /// Intern - Copy this node's data to a memory region allocated from the
    174 /// given allocator and return a FoldingSetNodeIDRef describing the
    175 /// interned data.
    176 FoldingSetNodeIDRef
    177 FoldingSetNodeID::Intern(BumpPtrAllocator &Allocator) const {
    178   unsigned *New = Allocator.Allocate<unsigned>(Bits.size());
    179   std::uninitialized_copy(Bits.begin(), Bits.end(), New);
    180   return FoldingSetNodeIDRef(New, Bits.size());
    181 }
    182 
    183 //===----------------------------------------------------------------------===//
    184 /// Helper functions for FoldingSetImpl.
    185 
    186 /// GetNextPtr - In order to save space, each bucket is a
    187 /// singly-linked-list. In order to make deletion more efficient, we make
    188 /// the list circular, so we can delete a node without computing its hash.
    189 /// The problem with this is that the start of the hash buckets are not
    190 /// Nodes.  If NextInBucketPtr is a bucket pointer, this method returns null:
    191 /// use GetBucketPtr when this happens.
    192 static FoldingSetImpl::Node *GetNextPtr(void *NextInBucketPtr) {
    193   // The low bit is set if this is the pointer back to the bucket.
    194   if (reinterpret_cast<intptr_t>(NextInBucketPtr) & 1)
    195     return 0;
    196 
    197   return static_cast<FoldingSetImpl::Node*>(NextInBucketPtr);
    198 }
    199 
    200 
    201 /// testing.
    202 static void **GetBucketPtr(void *NextInBucketPtr) {
    203   intptr_t Ptr = reinterpret_cast<intptr_t>(NextInBucketPtr);
    204   assert((Ptr & 1) && "Not a bucket pointer");
    205   return reinterpret_cast<void**>(Ptr & ~intptr_t(1));
    206 }
    207 
    208 /// GetBucketFor - Hash the specified node ID and return the hash bucket for
    209 /// the specified ID.
    210 static void **GetBucketFor(unsigned Hash, void **Buckets, unsigned NumBuckets) {
    211   // NumBuckets is always a power of 2.
    212   unsigned BucketNum = Hash & (NumBuckets-1);
    213   return Buckets + BucketNum;
    214 }
    215 
    216 /// AllocateBuckets - Allocated initialized bucket memory.
    217 static void **AllocateBuckets(unsigned NumBuckets) {
    218   void **Buckets = static_cast<void**>(calloc(NumBuckets+1, sizeof(void*)));
    219   // Set the very last bucket to be a non-null "pointer".
    220   Buckets[NumBuckets] = reinterpret_cast<void*>(-1);
    221   return Buckets;
    222 }
    223 
    224 //===----------------------------------------------------------------------===//
    225 // FoldingSetImpl Implementation
    226 
    227 FoldingSetImpl::FoldingSetImpl(unsigned Log2InitSize) {
    228   assert(5 < Log2InitSize && Log2InitSize < 32 &&
    229          "Initial hash table size out of range");
    230   NumBuckets = 1 << Log2InitSize;
    231   Buckets = AllocateBuckets(NumBuckets);
    232   NumNodes = 0;
    233 }
    234 FoldingSetImpl::~FoldingSetImpl() {
    235   free(Buckets);
    236 }
    237 void FoldingSetImpl::clear() {
    238   // Set all but the last bucket to null pointers.
    239   memset(Buckets, 0, NumBuckets*sizeof(void*));
    240 
    241   // Set the very last bucket to be a non-null "pointer".
    242   Buckets[NumBuckets] = reinterpret_cast<void*>(-1);
    243 
    244   // Reset the node count to zero.
    245   NumNodes = 0;
    246 }
    247 
    248 /// GrowHashTable - Double the size of the hash table and rehash everything.
    249 ///
    250 void FoldingSetImpl::GrowHashTable() {
    251   void **OldBuckets = Buckets;
    252   unsigned OldNumBuckets = NumBuckets;
    253   NumBuckets <<= 1;
    254 
    255   // Clear out new buckets.
    256   Buckets = AllocateBuckets(NumBuckets);
    257   NumNodes = 0;
    258 
    259   // Walk the old buckets, rehashing nodes into their new place.
    260   FoldingSetNodeID TempID;
    261   for (unsigned i = 0; i != OldNumBuckets; ++i) {
    262     void *Probe = OldBuckets[i];
    263     if (!Probe) continue;
    264     while (Node *NodeInBucket = GetNextPtr(Probe)) {
    265       // Figure out the next link, remove NodeInBucket from the old link.
    266       Probe = NodeInBucket->getNextInBucket();
    267       NodeInBucket->SetNextInBucket(0);
    268 
    269       // Insert the node into the new bucket, after recomputing the hash.
    270       InsertNode(NodeInBucket,
    271                  GetBucketFor(ComputeNodeHash(NodeInBucket, TempID),
    272                               Buckets, NumBuckets));
    273       TempID.clear();
    274     }
    275   }
    276 
    277   free(OldBuckets);
    278 }
    279 
    280 /// FindNodeOrInsertPos - Look up the node specified by ID.  If it exists,
    281 /// return it.  If not, return the insertion token that will make insertion
    282 /// faster.
    283 FoldingSetImpl::Node
    284 *FoldingSetImpl::FindNodeOrInsertPos(const FoldingSetNodeID &ID,
    285                                      void *&InsertPos) {
    286   unsigned IDHash = ID.ComputeHash();
    287   void **Bucket = GetBucketFor(IDHash, Buckets, NumBuckets);
    288   void *Probe = *Bucket;
    289 
    290   InsertPos = 0;
    291 
    292   FoldingSetNodeID TempID;
    293   while (Node *NodeInBucket = GetNextPtr(Probe)) {
    294     if (NodeEquals(NodeInBucket, ID, IDHash, TempID))
    295       return NodeInBucket;
    296     TempID.clear();
    297 
    298     Probe = NodeInBucket->getNextInBucket();
    299   }
    300 
    301   // Didn't find the node, return null with the bucket as the InsertPos.
    302   InsertPos = Bucket;
    303   return 0;
    304 }
    305 
    306 /// InsertNode - Insert the specified node into the folding set, knowing that it
    307 /// is not already in the map.  InsertPos must be obtained from
    308 /// FindNodeOrInsertPos.
    309 void FoldingSetImpl::InsertNode(Node *N, void *InsertPos) {
    310   assert(N->getNextInBucket() == 0);
    311   // Do we need to grow the hashtable?
    312   if (NumNodes+1 > NumBuckets*2) {
    313     GrowHashTable();
    314     FoldingSetNodeID TempID;
    315     InsertPos = GetBucketFor(ComputeNodeHash(N, TempID), Buckets, NumBuckets);
    316   }
    317 
    318   ++NumNodes;
    319 
    320   /// The insert position is actually a bucket pointer.
    321   void **Bucket = static_cast<void**>(InsertPos);
    322 
    323   void *Next = *Bucket;
    324 
    325   // If this is the first insertion into this bucket, its next pointer will be
    326   // null.  Pretend as if it pointed to itself, setting the low bit to indicate
    327   // that it is a pointer to the bucket.
    328   if (Next == 0)
    329     Next = reinterpret_cast<void*>(reinterpret_cast<intptr_t>(Bucket)|1);
    330 
    331   // Set the node's next pointer, and make the bucket point to the node.
    332   N->SetNextInBucket(Next);
    333   *Bucket = N;
    334 }
    335 
    336 /// RemoveNode - Remove a node from the folding set, returning true if one was
    337 /// removed or false if the node was not in the folding set.
    338 bool FoldingSetImpl::RemoveNode(Node *N) {
    339   // Because each bucket is a circular list, we don't need to compute N's hash
    340   // to remove it.
    341   void *Ptr = N->getNextInBucket();
    342   if (Ptr == 0) return false;  // Not in folding set.
    343 
    344   --NumNodes;
    345   N->SetNextInBucket(0);
    346 
    347   // Remember what N originally pointed to, either a bucket or another node.
    348   void *NodeNextPtr = Ptr;
    349 
    350   // Chase around the list until we find the node (or bucket) which points to N.
    351   while (true) {
    352     if (Node *NodeInBucket = GetNextPtr(Ptr)) {
    353       // Advance pointer.
    354       Ptr = NodeInBucket->getNextInBucket();
    355 
    356       // We found a node that points to N, change it to point to N's next node,
    357       // removing N from the list.
    358       if (Ptr == N) {
    359         NodeInBucket->SetNextInBucket(NodeNextPtr);
    360         return true;
    361       }
    362     } else {
    363       void **Bucket = GetBucketPtr(Ptr);
    364       Ptr = *Bucket;
    365 
    366       // If we found that the bucket points to N, update the bucket to point to
    367       // whatever is next.
    368       if (Ptr == N) {
    369         *Bucket = NodeNextPtr;
    370         return true;
    371       }
    372     }
    373   }
    374 }
    375 
    376 /// GetOrInsertNode - If there is an existing simple Node exactly
    377 /// equal to the specified node, return it.  Otherwise, insert 'N' and it
    378 /// instead.
    379 FoldingSetImpl::Node *FoldingSetImpl::GetOrInsertNode(FoldingSetImpl::Node *N) {
    380   FoldingSetNodeID ID;
    381   GetNodeProfile(N, ID);
    382   void *IP;
    383   if (Node *E = FindNodeOrInsertPos(ID, IP))
    384     return E;
    385   InsertNode(N, IP);
    386   return N;
    387 }
    388 
    389 //===----------------------------------------------------------------------===//
    390 // FoldingSetIteratorImpl Implementation
    391 
    392 FoldingSetIteratorImpl::FoldingSetIteratorImpl(void **Bucket) {
    393   // Skip to the first non-null non-self-cycle bucket.
    394   while (*Bucket != reinterpret_cast<void*>(-1) &&
    395          (*Bucket == 0 || GetNextPtr(*Bucket) == 0))
    396     ++Bucket;
    397 
    398   NodePtr = static_cast<FoldingSetNode*>(*Bucket);
    399 }
    400 
    401 void FoldingSetIteratorImpl::advance() {
    402   // If there is another link within this bucket, go to it.
    403   void *Probe = NodePtr->getNextInBucket();
    404 
    405   if (FoldingSetNode *NextNodeInBucket = GetNextPtr(Probe))
    406     NodePtr = NextNodeInBucket;
    407   else {
    408     // Otherwise, this is the last link in this bucket.
    409     void **Bucket = GetBucketPtr(Probe);
    410 
    411     // Skip to the next non-null non-self-cycle bucket.
    412     do {
    413       ++Bucket;
    414     } while (*Bucket != reinterpret_cast<void*>(-1) &&
    415              (*Bucket == 0 || GetNextPtr(*Bucket) == 0));
    416 
    417     NodePtr = static_cast<FoldingSetNode*>(*Bucket);
    418   }
    419 }
    420 
    421 //===----------------------------------------------------------------------===//
    422 // FoldingSetBucketIteratorImpl Implementation
    423 
    424 FoldingSetBucketIteratorImpl::FoldingSetBucketIteratorImpl(void **Bucket) {
    425   Ptr = (*Bucket == 0 || GetNextPtr(*Bucket) == 0) ? (void*) Bucket : *Bucket;
    426 }
    427