| //===-- Support/FoldingSet.cpp - Uniquing Hash Set --------------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file implements a hash set that can be used to remove duplication of |
| // nodes in a graph. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/ADT/FoldingSet.h" |
| #include "llvm/ADT/STLExtras.h" |
| #include "llvm/ADT/StringRef.h" |
| #include "llvm/Support/Allocator.h" |
| #include "llvm/Support/MathExtras.h" |
| #include "llvm/Support/SwapByteOrder.h" |
| #include <cassert> |
| #include <cstring> |
| using namespace llvm; |
| |
| //===----------------------------------------------------------------------===// |
| // FoldingSetNodeIDRef Implementation |
| |
| bool llvm::operator<(FoldingSetNodeIDRef LHS, FoldingSetNodeIDRef RHS) { |
| if (LHS.size() != RHS.size()) |
| return LHS.size() < RHS.size(); |
| return memcmp(LHS.data(), RHS.data(), LHS.size() * sizeof(unsigned)) < 0; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // FoldingSetNodeID Implementation |
| |
| void FoldingSetNodeID::AddString(StringRef String) { |
| unsigned Size = String.size(); |
| |
| unsigned NumInserts = 1 + divideCeil(Size, 4); |
| Bits.reserve(Bits.size() + NumInserts); |
| |
| Bits.push_back(Size); |
| if (!Size) |
| return; |
| |
| unsigned Units = Size / 4; |
| unsigned Pos = 0; |
| const unsigned *Base = (const unsigned *)String.data(); |
| |
| // If the string is aligned do a bulk transfer. |
| if (!((intptr_t)Base & 3)) { |
| Bits.append(Base, Base + Units); |
| Pos = (Units + 1) * 4; |
| } else { |
| // Otherwise do it the hard way. |
| // To be compatible with above bulk transfer, we need to take endianness |
| // into account. |
| static_assert(sys::IsBigEndianHost || sys::IsLittleEndianHost, |
| "Unexpected host endianness"); |
| if (sys::IsBigEndianHost) { |
| for (Pos += 4; Pos <= Size; Pos += 4) { |
| unsigned V = ((unsigned char)String[Pos - 4] << 24) | |
| ((unsigned char)String[Pos - 3] << 16) | |
| ((unsigned char)String[Pos - 2] << 8) | |
| (unsigned char)String[Pos - 1]; |
| Bits.push_back(V); |
| } |
| } else { // Little-endian host |
| for (Pos += 4; Pos <= Size; Pos += 4) { |
| unsigned V = ((unsigned char)String[Pos - 1] << 24) | |
| ((unsigned char)String[Pos - 2] << 16) | |
| ((unsigned char)String[Pos - 3] << 8) | |
| (unsigned char)String[Pos - 4]; |
| Bits.push_back(V); |
| } |
| } |
| } |
| |
| // With the leftover bits. |
| unsigned V = 0; |
| // Pos will have overshot size by 4 - #bytes left over. |
| // No need to take endianness into account here - this is always executed. |
| switch (Pos - Size) { |
| case 1: |
| V = (V << 8) | (unsigned char)String[Size - 3]; |
| [[fallthrough]]; |
| case 2: |
| V = (V << 8) | (unsigned char)String[Size - 2]; |
| [[fallthrough]]; |
| case 3: |
| V = (V << 8) | (unsigned char)String[Size - 1]; |
| break; |
| default: |
| return; // Nothing left. |
| } |
| |
| Bits.push_back(V); |
| } |
| |
| void FoldingSetNodeID::AddNodeID(const FoldingSetNodeID &ID) { |
| Bits.append(ID.Bits.begin(), ID.Bits.end()); |
| } |
| |
| FoldingSetNodeIDRef |
| FoldingSetNodeID::Intern(BumpPtrAllocator &Allocator) const { |
| unsigned *New = Allocator.Allocate<unsigned>(Bits.size()); |
| llvm::uninitialized_copy(Bits, New); |
| return FoldingSetNodeIDRef(New, Bits.size()); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // FoldingSetBase Implementation |
| |
| FoldingSetBase::FoldingSetBase(unsigned Log2InitSize) { |
| assert(5 < Log2InitSize && Log2InitSize < 32 && |
| "Initial hash table size out of range"); |
| NumBuckets = 1 << Log2InitSize; |
| Buckets = static_cast<FoldingSetNode **>( |
| safe_calloc(NumBuckets, sizeof(FoldingSetNode *))); |
| } |
| |
| FoldingSetBase::FoldingSetBase(FoldingSetBase &&Arg) |
| : Buckets(std::exchange(Arg.Buckets, nullptr)), |
| NumBuckets(std::exchange(Arg.NumBuckets, 0)), |
| NumNodes(std::exchange(Arg.NumNodes, 0)) { |
| Arg.incrementEpoch(); |
| } |
| |
| FoldingSetBase &FoldingSetBase::operator=(FoldingSetBase &&RHS) { |
| if (this == &RHS) |
| return *this; |
| |
| incrementEpoch(); |
| RHS.incrementEpoch(); |
| free(Buckets); // This may be null if the set is in a moved-from state. |
| Buckets = std::exchange(RHS.Buckets, nullptr); |
| NumBuckets = std::exchange(RHS.NumBuckets, 0); |
| NumNodes = std::exchange(RHS.NumNodes, 0); |
| return *this; |
| } |
| |
| FoldingSetBase::~FoldingSetBase() { free(Buckets); } |
| |
| void FoldingSetBase::clear() { |
| incrementEpoch(); |
| // Stale hashes are unreachable, so only the occupancy needs resetting. |
| if (NumBuckets) |
| memset(Buckets, 0, NumBuckets * sizeof(FoldingSetNode *)); |
| NumNodes = 0; |
| } |
| |
| void FoldingSetBase::placeNode(FoldingSetNode *N, uint32_t Hash) { |
| unsigned Mask = NumBuckets - 1; |
| unsigned I = Hash & Mask; |
| while (Buckets[I]) { |
| assert(Buckets[I] != N && "Node already in the folding set"); |
| I = (I + 1) & Mask; |
| } |
| Buckets[I] = N; |
| ++NumNodes; |
| } |
| |
| void FoldingSetBase::grow(unsigned MinNumBuckets) { |
| // The floor is the smallest size the constructor accepts. |
| unsigned NewBucketCount = std::max(64u, llvm::bit_ceil(MinNumBuckets)); |
| assert(NewBucketCount > NumBuckets && "Can't shrink a folding set"); |
| |
| FoldingSetBase Tmp(llvm::Log2_32(NewBucketCount)); |
| for (unsigned I = 0; I != NumBuckets; ++I) |
| if (FoldingSetNode *N = Buckets[I]) |
| Tmp.placeNode(N, N->getFoldingSetHash()); |
| |
| *this = std::move(Tmp); |
| } |
| |
| void FoldingSetBase::reserve(unsigned N) { |
| if (N * 4 <= NumBuckets * 3) |
| return; |
| // N + (N + 2) / 3 is ceil(4N/3). |
| grow(N + (N + 2) / 3); |
| } |
| |
| void FoldingSetBase::insert(FoldingSetNode *N, FoldingSetInsertToken Token) { |
| assert(N && "Cannot insert a null node"); |
| assert(Token && "Invalid token!"); |
| incrementEpoch(); |
| if (LLVM_UNLIKELY((NumNodes + 1) * 4 > NumBuckets * 3)) |
| grow(NumBuckets * 2); |
| uint32_t Hash = Token.Hash; |
| placeNode(N, Hash); |
| N->setFoldingSetHash(Hash); |
| } |
| |
| bool FoldingSetBase::erase(FoldingSetNode *N) { |
| uint32_t Hash = N->getFoldingSetHash(); |
| if (Hash == FoldingSetNodeIDRef::NotAHash) |
| return false; // Never inserted. |
| |
| unsigned Mask = NumBuckets - 1; |
| unsigned I = Hash & Mask; |
| while (Buckets[I] != N) { |
| if (LLVM_UNLIKELY(!Buckets[I])) |
| return false; // Not in folding set. |
| I = (I + 1) & Mask; |
| } |
| |
| incrementEpoch(); |
| |
| // Knuth TAOCP 6.4 Algorithm R: walk forward sliding each following entry |
| // whose probe path crosses the hole. |
| for (unsigned J = (I + 1) & Mask; Buckets[J]; J = (J + 1) & Mask) { |
| unsigned Ideal = Buckets[J]->getFoldingSetHash(); |
| if (((I - Ideal) & Mask) < ((J - Ideal) & Mask)) { |
| Buckets[I] = Buckets[J]; |
| I = J; |
| } |
| } |
| Buckets[I] = nullptr; |
| N->setFoldingSetHash(FoldingSetNodeIDRef::NotAHash); |
| --NumNodes; |
| return true; |
| } |