blob: 14cbdaede99da49d8434594876578c1d94459252 [file] [edit]
//===-- 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;
}