[upstream_utils] Upgrade to LLVM 20.1.7 (#8033)

Also removes xxhash, Hashing, and MapVector to reduce the size of the patches and to speed up compile times by a smidge.
This commit is contained in:
Gold856
2025-06-25 01:36:22 -04:00
committed by GitHub
parent a77441b78a
commit 22b58c1853
73 changed files with 1154 additions and 3813 deletions

View File

@@ -24,7 +24,7 @@
#ifndef _WIN32
#include <unistd.h>
#endif
#if defined(_MSC_VER)
#if defined(_WIN32)
#include <io.h>
#endif

View File

@@ -13,12 +13,15 @@
LLVM_ATTRIBUTE_RETURNS_NONNULL LLVM_ATTRIBUTE_RETURNS_NOALIAS void *
wpi::allocate_buffer(size_t Size, size_t Alignment) {
return ::operator new(Size
void *Result = ::operator new(Size,
#ifdef __cpp_aligned_new
,
std::align_val_t(Alignment)
std::align_val_t(Alignment),
#endif
);
std::nothrow);
if (Result == nullptr) {
report_bad_alloc_error("Buffer allocation failed");
}
return Result;
}
void wpi::deallocate_buffer(void *Ptr, size_t Size, size_t Alignment) {

View File

@@ -62,7 +62,25 @@ SmallPtrSetImplBase::insert_imp_big(const void *Ptr) {
return std::make_pair(Bucket, true);
}
const void * const *SmallPtrSetImplBase::FindBucketFor(const void *Ptr) const {
const void *const *SmallPtrSetImplBase::doFind(const void *Ptr) const {
unsigned BucketNo =
DenseMapInfo<void *>::getHashValue(Ptr) & (CurArraySize - 1);
unsigned ProbeAmt = 1;
while (true) {
const void *const *Bucket = CurArray + BucketNo;
if (LLVM_LIKELY(*Bucket == Ptr))
return Bucket;
if (LLVM_LIKELY(*Bucket == getEmptyMarker()))
return nullptr;
// Otherwise, it's a hash collision or a tombstone, continue quadratic
// probing.
BucketNo += ProbeAmt++;
BucketNo &= CurArraySize - 1;
}
}
const void *const *SmallPtrSetImplBase::FindBucketFor(const void *Ptr) const {
unsigned Bucket = DenseMapInfo<void *>::getHashValue(Ptr) & (CurArraySize-1);
unsigned ArraySize = CurArraySize;
unsigned ProbeAmt = 1;
@@ -116,32 +134,33 @@ void SmallPtrSetImplBase::Grow(unsigned NewSize) {
free(OldBuckets);
NumNonEmpty -= NumTombstones;
NumTombstones = 0;
IsSmall = false;
}
SmallPtrSetImplBase::SmallPtrSetImplBase(const void **SmallStorage,
const SmallPtrSetImplBase &that) {
SmallArray = SmallStorage;
// If we're becoming small, prepare to insert into our stack space
if (that.isSmall()) {
CurArray = SmallArray;
// Otherwise, allocate new heap space (unless we were the same size)
IsSmall = that.isSmall();
if (IsSmall) {
// If we're becoming small, prepare to insert into our stack space
CurArray = SmallStorage;
} else {
// Otherwise, allocate new heap space (unless we were the same size)
CurArray = (const void**)safe_malloc(sizeof(void*) * that.CurArraySize);
}
// Copy over the that array.
CopyHelper(that);
copyHelper(that);
}
SmallPtrSetImplBase::SmallPtrSetImplBase(const void **SmallStorage,
unsigned SmallSize,
const void **RHSSmallStorage,
SmallPtrSetImplBase &&that) {
SmallArray = SmallStorage;
MoveHelper(SmallSize, std::move(that));
moveHelper(SmallStorage, SmallSize, RHSSmallStorage, std::move(that));
}
void SmallPtrSetImplBase::CopyFrom(const SmallPtrSetImplBase &RHS) {
void SmallPtrSetImplBase::copyFrom(const void **SmallStorage,
const SmallPtrSetImplBase &RHS) {
assert(&RHS != this && "Self-copy should be handled by the caller.");
if (isSmall() && RHS.isSmall())
@@ -152,8 +171,9 @@ void SmallPtrSetImplBase::CopyFrom(const SmallPtrSetImplBase &RHS) {
if (RHS.isSmall()) {
if (!isSmall())
free(CurArray);
CurArray = SmallArray;
// Otherwise, allocate new heap space (unless we were the same size)
CurArray = SmallStorage;
IsSmall = true;
// Otherwise, allocate new heap space (unless we were the same size)
} else if (CurArraySize != RHS.CurArraySize) {
if (isSmall())
CurArray = (const void**)safe_malloc(sizeof(void*) * RHS.CurArraySize);
@@ -162,12 +182,13 @@ void SmallPtrSetImplBase::CopyFrom(const SmallPtrSetImplBase &RHS) {
sizeof(void*) * RHS.CurArraySize);
CurArray = T;
}
IsSmall = false;
}
CopyHelper(RHS);
copyHelper(RHS);
}
void SmallPtrSetImplBase::CopyHelper(const SmallPtrSetImplBase &RHS) {
void SmallPtrSetImplBase::copyHelper(const SmallPtrSetImplBase &RHS) {
// Copy over the new array size
CurArraySize = RHS.CurArraySize;
@@ -178,39 +199,46 @@ void SmallPtrSetImplBase::CopyHelper(const SmallPtrSetImplBase &RHS) {
NumTombstones = RHS.NumTombstones;
}
void SmallPtrSetImplBase::MoveFrom(unsigned SmallSize,
void SmallPtrSetImplBase::moveFrom(const void **SmallStorage,
unsigned SmallSize,
const void **RHSSmallStorage,
SmallPtrSetImplBase &&RHS) {
if (!isSmall())
free(CurArray);
MoveHelper(SmallSize, std::move(RHS));
moveHelper(SmallStorage, SmallSize, RHSSmallStorage, std::move(RHS));
}
void SmallPtrSetImplBase::MoveHelper(unsigned SmallSize,
void SmallPtrSetImplBase::moveHelper(const void **SmallStorage,
unsigned SmallSize,
const void **RHSSmallStorage,
SmallPtrSetImplBase &&RHS) {
assert(&RHS != this && "Self-move should be handled by the caller.");
if (RHS.isSmall()) {
// Copy a small RHS rather than moving.
CurArray = SmallArray;
CurArray = SmallStorage;
std::copy(RHS.CurArray, RHS.CurArray + RHS.NumNonEmpty, CurArray);
} else {
CurArray = RHS.CurArray;
RHS.CurArray = RHS.SmallArray;
RHS.CurArray = RHSSmallStorage;
}
// Copy the rest of the trivial members.
CurArraySize = RHS.CurArraySize;
NumNonEmpty = RHS.NumNonEmpty;
NumTombstones = RHS.NumTombstones;
IsSmall = RHS.IsSmall;
// Make the RHS small and empty.
RHS.CurArraySize = SmallSize;
assert(RHS.CurArray == RHS.SmallArray);
RHS.NumNonEmpty = 0;
RHS.NumTombstones = 0;
RHS.IsSmall = true;
}
void SmallPtrSetImplBase::swap(SmallPtrSetImplBase &RHS) {
void SmallPtrSetImplBase::swap(const void **SmallStorage,
const void **RHSSmallStorage,
SmallPtrSetImplBase &RHS) {
if (this == &RHS) return;
// We can only avoid copying elements if neither set is small.
@@ -227,42 +255,42 @@ void SmallPtrSetImplBase::swap(SmallPtrSetImplBase &RHS) {
// If only RHS is small, copy the small elements into LHS and move the pointer
// from LHS to RHS.
if (!this->isSmall() && RHS.isSmall()) {
assert(RHS.CurArray == RHS.SmallArray);
std::copy(RHS.CurArray, RHS.CurArray + RHS.NumNonEmpty, this->SmallArray);
std::copy(RHS.CurArray, RHS.CurArray + RHS.NumNonEmpty, SmallStorage);
std::swap(RHS.CurArraySize, this->CurArraySize);
std::swap(this->NumNonEmpty, RHS.NumNonEmpty);
std::swap(this->NumTombstones, RHS.NumTombstones);
RHS.CurArray = this->CurArray;
this->CurArray = this->SmallArray;
RHS.IsSmall = false;
this->CurArray = SmallStorage;
this->IsSmall = true;
return;
}
// If only LHS is small, copy the small elements into RHS and move the pointer
// from RHS to LHS.
if (this->isSmall() && !RHS.isSmall()) {
assert(this->CurArray == this->SmallArray);
std::copy(this->CurArray, this->CurArray + this->NumNonEmpty,
RHS.SmallArray);
RHSSmallStorage);
std::swap(RHS.CurArraySize, this->CurArraySize);
std::swap(RHS.NumNonEmpty, this->NumNonEmpty);
std::swap(RHS.NumTombstones, this->NumTombstones);
this->CurArray = RHS.CurArray;
RHS.CurArray = RHS.SmallArray;
this->IsSmall = false;
RHS.CurArray = RHSSmallStorage;
RHS.IsSmall = true;
return;
}
// Both a small, just swap the small elements.
assert(this->isSmall() && RHS.isSmall());
unsigned MinNonEmpty = std::min(this->NumNonEmpty, RHS.NumNonEmpty);
std::swap_ranges(this->SmallArray, this->SmallArray + MinNonEmpty,
RHS.SmallArray);
std::swap_ranges(this->CurArray, this->CurArray + MinNonEmpty, RHS.CurArray);
if (this->NumNonEmpty > MinNonEmpty) {
std::copy(this->SmallArray + MinNonEmpty,
this->SmallArray + this->NumNonEmpty,
RHS.SmallArray + MinNonEmpty);
std::copy(this->CurArray + MinNonEmpty, this->CurArray + this->NumNonEmpty,
RHS.CurArray + MinNonEmpty);
} else {
std::copy(RHS.SmallArray + MinNonEmpty, RHS.SmallArray + RHS.NumNonEmpty,
this->SmallArray + MinNonEmpty);
std::copy(RHS.CurArray + MinNonEmpty, RHS.CurArray + RHS.NumNonEmpty,
this->CurArray + MinNonEmpty);
}
assert(this->CurArraySize == RHS.CurArraySize);
std::swap(this->NumNonEmpty, RHS.NumNonEmpty);

View File

@@ -102,9 +102,18 @@ static size_t getNewCapacity(size_t MinSize, size_t TSize, size_t OldCapacity) {
return std::clamp(NewCapacity, MinSize, MaxSize);
}
void *SmallVectorBase::replaceAllocation(void *NewElts, size_t TSize,
size_t NewCapacity,
size_t VSize) {
/// If vector was first created with capacity 0, getFirstEl() points to the
/// memory right after, an area unallocated. If a subsequent allocation,
/// that grows the vector, happens to return the same pointer as getFirstEl(),
/// get a new allocation, otherwise isSmall() will falsely return that no
/// allocation was done (true) and the memory will not be freed in the
/// destructor. If a VSize is given (vector size), also copy that many
/// elements to the new allocation - used if realloca fails to increase
/// space, and happens to allocate precisely at BeginX.
/// This is unlikely to be called often, but resolves a memory leak when the
/// situation does occur.
static void *replaceAllocation(void *NewElts, size_t TSize, size_t NewCapacity,
size_t VSize = 0) {
void *NewEltsReplace = wpi::safe_malloc(NewCapacity * TSize);
if (VSize)
memcpy(NewEltsReplace, NewElts, VSize * TSize);

View File

@@ -23,11 +23,9 @@
// mingw-w64 tends to define it as 0x0502 in its headers.
#undef _WIN32_WINNT
#undef _WIN32_IE
// Require at least Windows 7 API.
#define _WIN32_WINNT 0x0601
#define _WIN32_IE 0x0800 // MinGW at it again. FIXME: verify if still needed.
#define WIN32_LEAN_AND_MEAN
#ifndef NOMINMAX
#define NOMINMAX

View File

@@ -70,14 +70,6 @@ constexpr raw_ostream::Colors raw_ostream::WHITE;
constexpr raw_ostream::Colors raw_ostream::SAVEDCOLOR;
constexpr raw_ostream::Colors raw_ostream::RESET;
namespace {
// Find the length of an array.
template <class T, std::size_t N>
constexpr inline size_t array_lengthof(T (&)[N]) {
return N;
}
} // namespace
raw_ostream::~raw_ostream() {
// raw_ostream's subclasses should take care to flush the buffer
// in their destructors.
@@ -575,6 +567,10 @@ size_t raw_fd_ostream::preferred_buffer_size() const {
if (IsWindowsConsole)
return 0;
return raw_ostream::preferred_buffer_size();
#elif defined(__MVS__)
// The buffer size on z/OS is defined with macro BUFSIZ, which can be
// retrieved by invoking function raw_ostream::preferred_buffer_size().
return raw_ostream::preferred_buffer_size();
#else
assert(FD >= 0 && "File not yet open!");
struct stat statbuf;

File diff suppressed because it is too large Load Diff

View File

@@ -755,7 +755,7 @@ template <class X, class Y> auto dyn_cast_if_present(Y *Val) {
// Forwards to dyn_cast_if_present to avoid breaking current users. This is
// deprecated and will be removed in a future patch, use
// cast_if_present instead.
// dyn_cast_if_present instead.
template <class X, class Y> auto dyn_cast_or_null(const Y &Val) {
return dyn_cast_if_present<X>(Val);
}

View File

@@ -38,10 +38,6 @@
# define __has_builtin(x) 0
#endif
#ifndef __has_include
# define __has_include(x) 0
#endif
// Only use __has_cpp_attribute in C++ mode. GCC defines __has_cpp_attribute in
// C mode, but the :: in __has_cpp_attribute(scoped::attribute) is invalid.
#ifndef LLVM_HAS_CPP_ATTRIBUTE
@@ -115,7 +111,8 @@
/// this attribute will be made public and visible outside of any shared library
/// they are linked in to.
#if LLVM_HAS_CPP_ATTRIBUTE(gnu::visibility)
#if LLVM_HAS_CPP_ATTRIBUTE(gnu::visibility) && defined(__GNUC__) && \
!defined(__clang__)
#define LLVM_ATTRIBUTE_VISIBILITY_HIDDEN [[gnu::visibility("hidden")]]
#define LLVM_ATTRIBUTE_VISIBILITY_DEFAULT [[gnu::visibility("default")]]
#elif __has_attribute(visibility)
@@ -126,18 +123,100 @@
#define LLVM_ATTRIBUTE_VISIBILITY_DEFAULT
#endif
#if (!(defined(_WIN32) || defined(__CYGWIN__)) || \
(defined(__MINGW32__) && defined(__clang__)))
#define LLVM_LIBRARY_VISIBILITY LLVM_ATTRIBUTE_VISIBILITY_HIDDEN
#if defined(LLVM_BUILD_LLVM_DYLIB) || defined(LLVM_BUILD_SHARED_LIBS)
#define LLVM_EXTERNAL_VISIBILITY LLVM_ATTRIBUTE_VISIBILITY_DEFAULT
#else
#define LLVM_EXTERNAL_VISIBILITY
#endif
#if (!(defined(_WIN32) || defined(__CYGWIN__)) || \
(defined(__MINGW32__) && defined(__clang__)))
#define LLVM_LIBRARY_VISIBILITY LLVM_ATTRIBUTE_VISIBILITY_HIDDEN
// Clang compilers older then 15 do not support gnu style attributes on
// namespaces.
#if defined(__clang__) && __clang_major__ < 15
#define LLVM_LIBRARY_VISIBILITY_NAMESPACE [[gnu::visibility("hidden")]]
#else
#define LLVM_LIBRARY_VISIBILITY_NAMESPACE LLVM_ATTRIBUTE_VISIBILITY_HIDDEN
#endif
#define LLVM_ALWAYS_EXPORT LLVM_ATTRIBUTE_VISIBILITY_DEFAULT
#elif defined(_WIN32)
#define LLVM_ALWAYS_EXPORT __declspec(dllexport)
#define LLVM_LIBRARY_VISIBILITY
#define LLVM_LIBRARY_VISIBILITY_NAMESPACE
#else
#define LLVM_LIBRARY_VISIBILITY
#define LLVM_EXTERNAL_VISIBILITY
#define LLVM_ALWAYS_EXPORT
#define LLVM_LIBRARY_VISIBILITY_NAMESPACE
#endif
/// LLVM_ABI is the main export/visibility macro to mark something as explicitly
/// exported when llvm is built as a shared library with everything else that is
/// unannotated will have internal visibility.
///
/// LLVM_ABI_EXPORT is for the special case for things like plugin symbol
/// declarations or definitions where we don't want the macro to be switching
/// between dllexport and dllimport on windows based on what codebase is being
/// built, it will only be dllexport. For non windows platforms this macro
/// behaves the same as LLVM_ABI.
///
/// LLVM_EXPORT_TEMPLATE is used on explicit template instantiations in source
/// files that were declared extern in a header. This macro is only set as a
/// compiler export attribute on windows, on other platforms it does nothing.
///
/// LLVM_TEMPLATE_ABI is for annotating extern template declarations in headers
/// for both functions and classes. On windows its turned in to dllimport for
/// library consumers, for other platforms its a default visibility attribute.
///
/// LLVM_C_ABI is used to annotated functions and data that need to be exported
/// for the libllvm-c API. This used both for the llvm-c headers and for the
/// functions declared in the different Target's c++ source files that don't
/// include the header forward declaring them.
#ifndef LLVM_ABI_GENERATING_ANNOTATIONS
// Marker to add to classes or functions in public headers that should not have
// export macros added to them by the clang tool
#define LLVM_ABI_NOT_EXPORTED
#if defined(LLVM_BUILD_LLVM_DYLIB) || defined(LLVM_BUILD_SHARED_LIBS) || \
defined(LLVM_ENABLE_PLUGINS)
// Some libraries like those for tablegen are linked in to tools that used
// in the build so can't depend on the llvm shared library. If export macros
// were left enabled when building these we would get duplicate or
// missing symbol linker errors on windows.
#if defined(LLVM_BUILD_STATIC)
#define LLVM_ABI
#define LLVM_TEMPLATE_ABI
#define LLVM_EXPORT_TEMPLATE
#define LLVM_ABI_EXPORT
#elif defined(_WIN32) && !defined(__MINGW32__)
#if defined(LLVM_EXPORTS)
#define LLVM_ABI __declspec(dllexport)
#define LLVM_TEMPLATE_ABI
#define LLVM_EXPORT_TEMPLATE __declspec(dllexport)
#else
#define LLVM_ABI __declspec(dllimport)
#define LLVM_TEMPLATE_ABI __declspec(dllimport)
#define LLVM_EXPORT_TEMPLATE
#endif
#define LLVM_ABI_EXPORT __declspec(dllexport)
#elif defined(__ELF__) || defined(__MINGW32__) || defined(_AIX) || \
defined(__MVS__)
#define LLVM_ABI LLVM_ATTRIBUTE_VISIBILITY_DEFAULT
#define LLVM_TEMPLATE_ABI LLVM_ATTRIBUTE_VISIBILITY_DEFAULT
#define LLVM_EXPORT_TEMPLATE
#define LLVM_ABI_EXPORT LLVM_ATTRIBUTE_VISIBILITY_DEFAULT
#elif defined(__MACH__) || defined(__WASM__) || defined(__EMSCRIPTEN__)
#define LLVM_ABI LLVM_ATTRIBUTE_VISIBILITY_DEFAULT
#define LLVM_TEMPLATE_ABI
#define LLVM_EXPORT_TEMPLATE
#define LLVM_ABI_EXPORT LLVM_ATTRIBUTE_VISIBILITY_DEFAULT
#endif
#else
#define LLVM_ABI
#define LLVM_TEMPLATE_ABI
#define LLVM_EXPORT_TEMPLATE
#define LLVM_ABI_EXPORT
#endif
#define LLVM_C_ABI LLVM_ABI
#endif
#ifndef LLVM_PREFETCH
@@ -357,6 +436,12 @@
#define LLVM_GSL_POINTER
#endif
#if LLVM_HAS_CPP_ATTRIBUTE(clang::lifetimebound)
#define LLVM_LIFETIME_BOUND [[clang::lifetimebound]]
#else
#define LLVM_LIFETIME_BOUND
#endif
#if LLVM_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L
#define LLVM_CTOR_NODISCARD [[nodiscard]]
#else

View File

@@ -110,7 +110,8 @@ public:
void clear() {
incrementEpoch();
if (getNumEntries() == 0 && getNumTombstones() == 0) return;
if (getNumEntries() == 0 && getNumTombstones() == 0)
return;
// If the capacity of the array is huge, and the # elements used is small,
// shrink the array.
@@ -119,12 +120,13 @@ public:
return;
}
const KeyT EmptyKey = getEmptyKey(), TombstoneKey = getTombstoneKey();
if (std::is_trivially_destructible<ValueT>::value) {
const KeyT EmptyKey = getEmptyKey();
if constexpr (std::is_trivially_destructible_v<ValueT>) {
// Use a simpler loop when values don't need destruction.
for (BucketT *P = getBuckets(), *E = getBucketsEnd(); P != E; ++P)
P->getFirst() = EmptyKey;
} else {
const KeyT TombstoneKey = getTombstoneKey();
[[maybe_unused]] unsigned NumEntries = getNumEntries();
for (BucketT *P = getBuckets(), *E = getBucketsEnd(); P != E; ++P) {
if (!KeyInfoT::isEqual(P->getFirst(), EmptyKey)) {
@@ -144,8 +146,7 @@ public:
/// Return true if the specified key is in the map, false otherwise.
bool contains(const_arg_type_t<KeyT> Val) const {
const BucketT *TheBucket;
return LookupBucketFor(Val, TheBucket);
return doFind(Val) != nullptr;
}
/// Return 1 if the specified key is in the map, 0 otherwise.
@@ -154,21 +155,17 @@ public:
}
iterator find(const_arg_type_t<KeyT> Val) {
BucketT *TheBucket;
if (LookupBucketFor(Val, TheBucket))
return makeIterator(TheBucket,
shouldReverseIterate<KeyT>() ? getBuckets()
: getBucketsEnd(),
*this, true);
if (BucketT *Bucket = doFind(Val))
return makeIterator(
Bucket, shouldReverseIterate<KeyT>() ? getBuckets() : getBucketsEnd(),
*this, true);
return end();
}
const_iterator find(const_arg_type_t<KeyT> Val) const {
const BucketT *TheBucket;
if (LookupBucketFor(Val, TheBucket))
return makeConstIterator(TheBucket,
shouldReverseIterate<KeyT>() ? getBuckets()
: getBucketsEnd(),
*this, true);
if (const BucketT *Bucket = doFind(Val))
return makeConstIterator(
Bucket, shouldReverseIterate<KeyT>() ? getBuckets() : getBucketsEnd(),
*this, true);
return end();
}
@@ -177,33 +174,27 @@ public:
/// The DenseMapInfo is responsible for supplying methods
/// getHashValue(LookupKeyT) and isEqual(LookupKeyT, KeyT) for each key
/// type used.
template<class LookupKeyT>
iterator find_as(const LookupKeyT &Val) {
BucketT *TheBucket;
if (LookupBucketFor(Val, TheBucket))
return makeIterator(TheBucket,
shouldReverseIterate<KeyT>() ? getBuckets()
: getBucketsEnd(),
*this, true);
template <class LookupKeyT> iterator find_as(const LookupKeyT &Val) {
if (BucketT *Bucket = doFind(Val))
return makeIterator(
Bucket, shouldReverseIterate<KeyT>() ? getBuckets() : getBucketsEnd(),
*this, true);
return end();
}
template<class LookupKeyT>
template <class LookupKeyT>
const_iterator find_as(const LookupKeyT &Val) const {
const BucketT *TheBucket;
if (LookupBucketFor(Val, TheBucket))
return makeConstIterator(TheBucket,
shouldReverseIterate<KeyT>() ? getBuckets()
: getBucketsEnd(),
*this, true);
if (const BucketT *Bucket = doFind(Val))
return makeConstIterator(
Bucket, shouldReverseIterate<KeyT>() ? getBuckets() : getBucketsEnd(),
*this, true);
return end();
}
/// lookup - Return the entry for the specified key, or a default
/// constructed value if no such entry exists.
ValueT lookup(const_arg_type_t<KeyT> Val) const {
const BucketT *TheBucket;
if (LookupBucketFor(Val, TheBucket))
return TheBucket->getSecond();
if (const BucketT *Bucket = doFind(Val))
return Bucket->getSecond();
return ValueT();
}
@@ -233,7 +224,7 @@ public:
// The value is constructed in-place if the key is not in the map, otherwise
// it is not moved.
template <typename... Ts>
std::pair<iterator, bool> try_emplace(KeyT &&Key, Ts &&... Args) {
std::pair<iterator, bool> try_emplace(KeyT &&Key, Ts &&...Args) {
BucketT *TheBucket;
if (LookupBucketFor(Key, TheBucket))
return std::make_pair(makeIterator(TheBucket,
@@ -258,7 +249,7 @@ public:
// The value is constructed in-place if the key is not in the map, otherwise
// it is not moved.
template <typename... Ts>
std::pair<iterator, bool> try_emplace(const KeyT &Key, Ts &&... Args) {
std::pair<iterator, bool> try_emplace(const KeyT &Key, Ts &&...Args) {
BucketT *TheBucket;
if (LookupBucketFor(Key, TheBucket))
return std::make_pair(makeIterator(TheBucket,
@@ -307,8 +298,7 @@ public:
}
/// insert - Range insertion of pairs.
template<typename InputIt>
void insert(InputIt I, InputIt E) {
template <typename InputIt> void insert(InputIt I, InputIt E) {
for (; I != E; ++I)
insert(*I);
}
@@ -329,23 +319,9 @@ public:
return Ret;
}
/// Returns the value associated to the key in the map if it exists. If it
/// does not exist, emplace a default value for the key and returns a
/// reference to the newly created value.
ValueT &getOrInsertDefault(KeyT &&Key) {
return try_emplace(Key).first->second;
}
/// Returns the value associated to the key in the map if it exists. If it
/// does not exist, emplace a default value for the key and returns a
/// reference to the newly created value.
ValueT &getOrInsertDefault(const KeyT &Key) {
return try_emplace(Key).first->second;
}
bool erase(const KeyT &Val) {
BucketT *TheBucket;
if (!LookupBucketFor(Val, TheBucket))
BucketT *TheBucket = doFind(Val);
if (!TheBucket)
return false; // not in map.
TheBucket->getSecond().~ValueT();
@@ -362,28 +338,20 @@ public:
incrementNumTombstones();
}
value_type& FindAndConstruct(const KeyT &Key) {
BucketT *TheBucket;
if (LookupBucketFor(Key, TheBucket))
return *TheBucket;
return *InsertIntoBucket(TheBucket, Key);
}
ValueT &operator[](const KeyT &Key) {
return FindAndConstruct(Key).second;
}
value_type& FindAndConstruct(KeyT &&Key) {
BucketT *TheBucket;
if (LookupBucketFor(Key, TheBucket))
return *TheBucket;
return TheBucket->second;
return *InsertIntoBucket(TheBucket, std::move(Key));
return InsertIntoBucket(TheBucket, Key)->second;
}
ValueT &operator[](KeyT &&Key) {
return FindAndConstruct(std::move(Key)).second;
BucketT *TheBucket;
if (LookupBucketFor(Key, TheBucket))
return TheBucket->second;
return InsertIntoBucket(TheBucket, std::move(Key))->second;
}
/// isPointerIntoBucketsArray - Return true if the specified pointer points
@@ -418,7 +386,7 @@ protected:
setNumEntries(0);
setNumTombstones(0);
assert((getNumBuckets() & (getNumBuckets()-1)) == 0 &&
assert((getNumBuckets() & (getNumBuckets() - 1)) == 0 &&
"# initial buckets must be a power of two!");
const KeyT EmptyKey = getEmptyKey();
for (BucketT *B = getBuckets(), *E = getBucketsEnd(); B != E; ++B)
@@ -470,44 +438,45 @@ protected:
setNumEntries(other.getNumEntries());
setNumTombstones(other.getNumTombstones());
if (std::is_trivially_copyable<KeyT>::value &&
std::is_trivially_copyable<ValueT>::value)
memcpy(reinterpret_cast<void *>(getBuckets()), other.getBuckets(),
getNumBuckets() * sizeof(BucketT));
else
for (size_t i = 0; i < getNumBuckets(); ++i) {
::new (&getBuckets()[i].getFirst())
KeyT(other.getBuckets()[i].getFirst());
if (!KeyInfoT::isEqual(getBuckets()[i].getFirst(), getEmptyKey()) &&
!KeyInfoT::isEqual(getBuckets()[i].getFirst(), getTombstoneKey()))
::new (&getBuckets()[i].getSecond())
ValueT(other.getBuckets()[i].getSecond());
BucketT *Buckets = getBuckets();
const BucketT *OtherBuckets = other.getBuckets();
const size_t NumBuckets = getNumBuckets();
if constexpr (std::is_trivially_copyable_v<KeyT> &&
std::is_trivially_copyable_v<ValueT>) {
memcpy(reinterpret_cast<void *>(Buckets), OtherBuckets,
NumBuckets * sizeof(BucketT));
} else {
const KeyT EmptyKey = getEmptyKey();
const KeyT TombstoneKey = getTombstoneKey();
for (size_t I = 0; I < NumBuckets; ++I) {
::new (&Buckets[I].getFirst()) KeyT(OtherBuckets[I].getFirst());
if (!KeyInfoT::isEqual(Buckets[I].getFirst(), EmptyKey) &&
!KeyInfoT::isEqual(Buckets[I].getFirst(), TombstoneKey))
::new (&Buckets[I].getSecond()) ValueT(OtherBuckets[I].getSecond());
}
}
}
static unsigned getHashValue(const KeyT &Val) {
return KeyInfoT::getHashValue(Val);
}
template<typename LookupKeyT>
template <typename LookupKeyT>
static unsigned getHashValue(const LookupKeyT &Val) {
return KeyInfoT::getHashValue(Val);
}
static const KeyT getEmptyKey() {
static_assert(std::is_base_of<DenseMapBase, DerivedT>::value,
static_assert(std::is_base_of_v<DenseMapBase, DerivedT>,
"Must pass the derived type to this template!");
return KeyInfoT::getEmptyKey();
}
static const KeyT getTombstoneKey() {
return KeyInfoT::getTombstoneKey();
}
static const KeyT getTombstoneKey() { return KeyInfoT::getTombstoneKey(); }
private:
iterator makeIterator(BucketT *P, BucketT *E,
DebugEpochBase &Epoch,
bool NoAdvance=false) {
iterator makeIterator(BucketT *P, BucketT *E, DebugEpochBase &Epoch,
bool NoAdvance = false) {
if (shouldReverseIterate<KeyT>()) {
BucketT *B = P == getBucketsEnd() ? getBuckets() : P + 1;
return iterator(B, E, Epoch, NoAdvance);
@@ -517,7 +486,7 @@ private:
const_iterator makeConstIterator(const BucketT *P, const BucketT *E,
const DebugEpochBase &Epoch,
const bool NoAdvance=false) const {
const bool NoAdvance = false) const {
if (shouldReverseIterate<KeyT>()) {
const BucketT *B = P == getBucketsEnd() ? getBuckets() : P + 1;
return const_iterator(B, E, Epoch, NoAdvance);
@@ -533,13 +502,9 @@ private:
static_cast<DerivedT *>(this)->setNumEntries(Num);
}
void incrementNumEntries() {
setNumEntries(getNumEntries() + 1);
}
void incrementNumEntries() { setNumEntries(getNumEntries() + 1); }
void decrementNumEntries() {
setNumEntries(getNumEntries() - 1);
}
void decrementNumEntries() { setNumEntries(getNumEntries() - 1); }
unsigned getNumTombstones() const {
return static_cast<const DerivedT *>(this)->getNumTombstones();
@@ -549,46 +514,34 @@ private:
static_cast<DerivedT *>(this)->setNumTombstones(Num);
}
void incrementNumTombstones() {
setNumTombstones(getNumTombstones() + 1);
}
void incrementNumTombstones() { setNumTombstones(getNumTombstones() + 1); }
void decrementNumTombstones() {
setNumTombstones(getNumTombstones() - 1);
}
void decrementNumTombstones() { setNumTombstones(getNumTombstones() - 1); }
const BucketT *getBuckets() const {
return static_cast<const DerivedT *>(this)->getBuckets();
}
BucketT *getBuckets() {
return static_cast<DerivedT *>(this)->getBuckets();
}
BucketT *getBuckets() { return static_cast<DerivedT *>(this)->getBuckets(); }
unsigned getNumBuckets() const {
return static_cast<const DerivedT *>(this)->getNumBuckets();
}
BucketT *getBucketsEnd() {
return getBuckets() + getNumBuckets();
}
BucketT *getBucketsEnd() { return getBuckets() + getNumBuckets(); }
const BucketT *getBucketsEnd() const {
return getBuckets() + getNumBuckets();
}
void grow(unsigned AtLeast) {
static_cast<DerivedT *>(this)->grow(AtLeast);
}
void grow(unsigned AtLeast) { static_cast<DerivedT *>(this)->grow(AtLeast); }
void shrink_and_clear() {
static_cast<DerivedT *>(this)->shrink_and_clear();
}
void shrink_and_clear() { static_cast<DerivedT *>(this)->shrink_and_clear(); }
template <typename KeyArg, typename... ValueArgs>
BucketT *InsertIntoBucket(BucketT *TheBucket, KeyArg &&Key,
ValueArgs &&... Values) {
TheBucket = InsertIntoBucketImpl(Key, Key, TheBucket);
ValueArgs &&...Values) {
TheBucket = InsertIntoBucketImpl(Key, TheBucket);
TheBucket->getFirst() = std::forward<KeyArg>(Key);
::new (&TheBucket->getSecond()) ValueT(std::forward<ValueArgs>(Values)...);
@@ -598,7 +551,7 @@ private:
template <typename LookupKeyT>
BucketT *InsertIntoBucketWithLookup(BucketT *TheBucket, KeyT &&Key,
ValueT &&Value, LookupKeyT &Lookup) {
TheBucket = InsertIntoBucketImpl(Key, Lookup, TheBucket);
TheBucket = InsertIntoBucketImpl(Lookup, TheBucket);
TheBucket->getFirst() = std::move(Key);
::new (&TheBucket->getSecond()) ValueT(std::move(Value));
@@ -606,8 +559,7 @@ private:
}
template <typename LookupKeyT>
BucketT *InsertIntoBucketImpl(const KeyT &Key, const LookupKeyT &Lookup,
BucketT *TheBucket) {
BucketT *InsertIntoBucketImpl(const LookupKeyT &Lookup, BucketT *TheBucket) {
incrementEpoch();
// If the load of the hash table is more than 3/4, or if fewer than 1/8 of
@@ -625,8 +577,9 @@ private:
this->grow(NumBuckets * 2);
LookupBucketFor(Lookup, TheBucket);
NumBuckets = getNumBuckets();
} else if (LLVM_UNLIKELY(NumBuckets-(NewNumEntries+getNumTombstones()) <=
NumBuckets/8)) {
} else if (LLVM_UNLIKELY(NumBuckets -
(NewNumEntries + getNumTombstones()) <=
NumBuckets / 8)) {
this->grow(NumBuckets);
LookupBucketFor(Lookup, TheBucket);
}
@@ -644,14 +597,41 @@ private:
return TheBucket;
}
template <typename LookupKeyT> BucketT *doFind(const LookupKeyT &Val) {
BucketT *BucketsPtr = getBuckets();
const unsigned NumBuckets = getNumBuckets();
if (NumBuckets == 0)
return nullptr;
const KeyT EmptyKey = getEmptyKey();
unsigned BucketNo = getHashValue(Val) & (NumBuckets - 1);
unsigned ProbeAmt = 1;
while (true) {
BucketT *Bucket = BucketsPtr + BucketNo;
if (LLVM_LIKELY(KeyInfoT::isEqual(Val, Bucket->getFirst())))
return Bucket;
if (LLVM_LIKELY(KeyInfoT::isEqual(Bucket->getFirst(), EmptyKey)))
return nullptr;
// Otherwise, it's a hash collision or a tombstone, continue quadratic
// probing.
BucketNo += ProbeAmt++;
BucketNo &= NumBuckets - 1;
}
}
template <typename LookupKeyT>
const BucketT *doFind(const LookupKeyT &Val) const {
return const_cast<DenseMapBase *>(this)->doFind(Val); // NOLINT
}
/// LookupBucketFor - Lookup the appropriate bucket for Val, returning it in
/// FoundBucket. If the bucket contains the key and a value, this returns
/// true, otherwise it returns a bucket with an empty marker or tombstone and
/// returns false.
template<typename LookupKeyT>
bool LookupBucketFor(const LookupKeyT &Val,
const BucketT *&FoundBucket) const {
const BucketT *BucketsPtr = getBuckets();
template <typename LookupKeyT>
bool LookupBucketFor(const LookupKeyT &Val, BucketT *&FoundBucket) {
BucketT *BucketsPtr = getBuckets();
const unsigned NumBuckets = getNumBuckets();
if (NumBuckets == 0) {
@@ -660,17 +640,17 @@ private:
}
// FoundTombstone - Keep track of whether we find a tombstone while probing.
const BucketT *FoundTombstone = nullptr;
BucketT *FoundTombstone = nullptr;
const KeyT EmptyKey = getEmptyKey();
const KeyT TombstoneKey = getTombstoneKey();
assert(!KeyInfoT::isEqual(Val, EmptyKey) &&
!KeyInfoT::isEqual(Val, TombstoneKey) &&
"Empty/Tombstone value shouldn't be inserted into map!");
unsigned BucketNo = getHashValue(Val) & (NumBuckets-1);
unsigned BucketNo = getHashValue(Val) & (NumBuckets - 1);
unsigned ProbeAmt = 1;
while (true) {
const BucketT *ThisBucket = BucketsPtr + BucketNo;
BucketT *ThisBucket = BucketsPtr + BucketNo;
// Found Val's bucket? If so, return it.
if (LLVM_LIKELY(KeyInfoT::isEqual(Val, ThisBucket->getFirst()))) {
FoundBucket = ThisBucket;
@@ -690,32 +670,21 @@ private:
// prefer to return it than something that would require more probing.
if (KeyInfoT::isEqual(ThisBucket->getFirst(), TombstoneKey) &&
!FoundTombstone)
FoundTombstone = ThisBucket; // Remember the first tombstone found.
FoundTombstone = ThisBucket; // Remember the first tombstone found.
// Otherwise, it's a hash collision or a tombstone, continue quadratic
// probing.
BucketNo += ProbeAmt++;
BucketNo &= (NumBuckets-1);
BucketNo &= (NumBuckets - 1);
}
}
template <typename LookupKeyT>
bool LookupBucketFor(const LookupKeyT &Val, BucketT *&FoundBucket) {
const BucketT *ConstFoundBucket;
bool Result = const_cast<const DenseMapBase *>(this)
->LookupBucketFor(Val, ConstFoundBucket);
FoundBucket = const_cast<BucketT *>(ConstFoundBucket);
return Result;
}
public:
/// Return the approximate size (in bytes) of the actual map.
/// This is just the raw memory used by DenseMap.
/// If entries are pointers to objects, the size of the referenced objects
/// are not included.
size_t getMemorySize() const {
return getNumBuckets() * sizeof(BucketT);
}
size_t getMemorySize() const { return getNumBuckets() * sizeof(BucketT); }
};
/// Equality comparison for DenseMap.
@@ -783,8 +752,7 @@ public:
swap(other);
}
template<typename InputIt>
DenseMap(const InputIt &I, const InputIt &E) {
template <typename InputIt> DenseMap(const InputIt &I, const InputIt &E) {
init(std::distance(I, E));
this->insert(I, E);
}
@@ -799,7 +767,7 @@ public:
deallocate_buffer(Buckets, sizeof(BucketT) * NumBuckets, alignof(BucketT));
}
void swap(DenseMap& RHS) {
void swap(DenseMap &RHS) {
this->incrementEpoch();
RHS.incrementEpoch();
std::swap(Buckets, RHS.Buckets);
@@ -808,13 +776,13 @@ public:
std::swap(NumBuckets, RHS.NumBuckets);
}
DenseMap& operator=(const DenseMap& other) {
DenseMap &operator=(const DenseMap &other) {
if (&other != this)
copyFrom(other);
return *this;
}
DenseMap& operator=(DenseMap &&other) {
DenseMap &operator=(DenseMap &&other) {
this->destroyAll();
deallocate_buffer(Buckets, sizeof(BucketT) * NumBuckets, alignof(BucketT));
init(0);
@@ -822,7 +790,7 @@ public:
return *this;
}
void copyFrom(const DenseMap& other) {
void copyFrom(const DenseMap &other) {
this->destroyAll();
deallocate_buffer(Buckets, sizeof(BucketT) * NumBuckets, alignof(BucketT));
if (allocateBuckets(other.NumBuckets)) {
@@ -847,14 +815,15 @@ public:
unsigned OldNumBuckets = NumBuckets;
BucketT *OldBuckets = Buckets;
allocateBuckets(std::max<unsigned>(64, static_cast<unsigned>(NextPowerOf2(AtLeast-1))));
allocateBuckets(std::max<unsigned>(
64, static_cast<unsigned>(NextPowerOf2(AtLeast - 1))));
assert(Buckets);
if (!OldBuckets) {
this->BaseT::initEmpty();
return;
}
this->moveFromOldBuckets(OldBuckets, OldBuckets+OldNumBuckets);
this->moveFromOldBuckets(OldBuckets, OldBuckets + OldNumBuckets);
// Free the old table.
deallocate_buffer(OldBuckets, sizeof(BucketT) * OldNumBuckets,
@@ -881,29 +850,17 @@ public:
}
private:
unsigned getNumEntries() const {
return NumEntries;
}
unsigned getNumEntries() const { return NumEntries; }
void setNumEntries(unsigned Num) {
NumEntries = Num;
}
void setNumEntries(unsigned Num) { NumEntries = Num; }
unsigned getNumTombstones() const {
return NumTombstones;
}
unsigned getNumTombstones() const { return NumTombstones; }
void setNumTombstones(unsigned Num) {
NumTombstones = Num;
}
void setNumTombstones(unsigned Num) { NumTombstones = Num; }
BucketT *getBuckets() const {
return Buckets;
}
BucketT *getBuckets() const { return Buckets; }
unsigned getNumBuckets() const {
return NumBuckets;
}
unsigned getNumBuckets() const { return NumBuckets; }
bool allocateBuckets(unsigned Num) {
NumBuckets = Num;
@@ -964,7 +921,7 @@ public:
swap(other);
}
template<typename InputIt>
template <typename InputIt>
SmallDenseMap(const InputIt &I, const InputIt &E) {
init(NextPowerOf2(std::distance(I, E)));
this->insert(I, E);
@@ -978,7 +935,7 @@ public:
deallocateBuckets();
}
void swap(SmallDenseMap& RHS) {
void swap(SmallDenseMap &RHS) {
unsigned TmpNumEntries = RHS.NumEntries;
RHS.NumEntries = NumEntries;
NumEntries = TmpNumEntries;
@@ -1050,13 +1007,13 @@ public:
new (SmallSide.getLargeRep()) LargeRep(std::move(TmpRep));
}
SmallDenseMap& operator=(const SmallDenseMap& other) {
SmallDenseMap &operator=(const SmallDenseMap &other) {
if (&other != this)
copyFrom(other);
return *this;
}
SmallDenseMap& operator=(SmallDenseMap &&other) {
SmallDenseMap &operator=(SmallDenseMap &&other) {
this->destroyAll();
deallocateBuckets();
init(0);
@@ -1064,7 +1021,7 @@ public:
return *this;
}
void copyFrom(const SmallDenseMap& other) {
void copyFrom(const SmallDenseMap &other) {
this->destroyAll();
deallocateBuckets();
Small = true;
@@ -1086,7 +1043,7 @@ public:
void grow(unsigned AtLeast) {
if (AtLeast > InlineBuckets)
AtLeast = std::max<unsigned>(64, NextPowerOf2(AtLeast-1));
AtLeast = std::max<unsigned>(64, NextPowerOf2(AtLeast - 1));
if (Small) {
// First move the inline buckets into a temporary storage.
@@ -1130,7 +1087,8 @@ public:
new (getLargeRep()) LargeRep(allocateBuckets(AtLeast));
}
this->moveFromOldBuckets(OldRep.Buckets, OldRep.Buckets+OldRep.NumBuckets);
this->moveFromOldBuckets(OldRep.Buckets,
OldRep.Buckets + OldRep.NumBuckets);
// Free the old table.
deallocate_buffer(OldRep.Buckets, sizeof(BucketT) * OldRep.NumBuckets,
@@ -1159,9 +1117,7 @@ public:
}
private:
unsigned getNumEntries() const {
return NumEntries;
}
unsigned getNumEntries() const { return NumEntries; }
void setNumEntries(unsigned Num) {
// NumEntries is hardcoded to be 31 bits wide.
@@ -1169,13 +1125,9 @@ private:
NumEntries = Num;
}
unsigned getNumTombstones() const {
return NumTombstones;
}
unsigned getNumTombstones() const { return NumTombstones; }
void setNumTombstones(unsigned Num) {
NumTombstones = Num;
}
void setNumTombstones(unsigned Num) { NumTombstones = Num; }
const BucketT *getInlineBuckets() const {
assert(Small);
@@ -1187,7 +1139,7 @@ private:
BucketT *getInlineBuckets() {
return const_cast<BucketT *>(
const_cast<const SmallDenseMap *>(this)->getInlineBuckets());
const_cast<const SmallDenseMap *>(this)->getInlineBuckets());
}
const LargeRep *getLargeRep() const {
@@ -1198,7 +1150,7 @@ private:
LargeRep *getLargeRep() {
return const_cast<LargeRep *>(
const_cast<const SmallDenseMap *>(this)->getLargeRep());
const_cast<const SmallDenseMap *>(this)->getLargeRep());
}
const BucketT *getBuckets() const {
@@ -1207,7 +1159,7 @@ private:
BucketT *getBuckets() {
return const_cast<BucketT *>(
const_cast<const SmallDenseMap *>(this)->getBuckets());
const_cast<const SmallDenseMap *>(this)->getBuckets());
}
unsigned getNumBuckets() const {
@@ -1258,7 +1210,8 @@ public:
: DebugEpochBase::HandleBase(&Epoch), Ptr(Pos), End(E) {
assert(isHandleInSync() && "invalid construction!");
if (NoAdvance) return;
if (NoAdvance)
return;
if (shouldReverseIterate<KeyT>()) {
RetreatPastEmptyBuckets();
return;
@@ -1304,7 +1257,7 @@ public:
return !(LHS == RHS);
}
inline DenseMapIterator& operator++() { // Preincrement
inline DenseMapIterator &operator++() { // Preincrement
assert(isHandleInSync() && "invalid iterator access!");
assert(Ptr != End && "incrementing end() iterator");
if (shouldReverseIterate<KeyT>()) {
@@ -1316,9 +1269,11 @@ public:
AdvancePastEmptyBuckets();
return *this;
}
DenseMapIterator operator++(int) { // Postincrement
DenseMapIterator operator++(int) { // Postincrement
assert(isHandleInSync() && "invalid iterator access!");
DenseMapIterator tmp = *this; ++*this; return tmp;
DenseMapIterator tmp = *this;
++*this;
return tmp;
}
private:

View File

@@ -60,7 +60,7 @@ template <typename value_type, std::size_t alignment = unaligned>
[[nodiscard]] inline value_type read(const void *memory, endianness endian) {
value_type ret;
memcpy(&ret,
memcpy(static_cast<void *>(&ret),
LLVM_ASSUME_ALIGNED(
memory, (detail::PickAlignment<value_type, alignment>::value)),
sizeof(value_type));

View File

@@ -87,6 +87,7 @@ using EnableIfCallable = std::enable_if_t<std::disjunction<
template <typename ReturnT, typename... ParamTs> class UniqueFunctionBase {
protected:
static constexpr size_t InlineStorageSize = sizeof(void *) * 4;
static constexpr size_t InlineStorageAlign = alignof(void *);
template <typename T, class = void>
struct IsSizeLessThanThresholdT : std::false_type {};
@@ -168,7 +169,8 @@ protected:
// provide four pointers worth of storage here.
// This is mutable as an inlined `const unique_function<void() const>` may
// still modify its own mutable members.
alignas(void *) mutable std::byte InlineStorage[InlineStorageSize];
alignas(InlineStorageAlign) mutable std::byte
InlineStorage[InlineStorageSize];
} StorageUnion;
// A compressed pointer to either our dispatching callback or our table of
@@ -269,7 +271,7 @@ protected:
bool IsInlineStorage = true;
void *CallableAddr = getInlineStorage();
if (sizeof(CallableT) > InlineStorageSize ||
alignof(CallableT) > alignof(decltype(StorageUnion.InlineStorage))) {
alignof(CallableT) > InlineStorageAlign) {
IsInlineStorage = false;
// Allocate out-of-line storage. FIXME: Use an explicit alignment
// parameter in C++17 mode.
@@ -319,6 +321,7 @@ protected:
// Non-trivial move, so dispatch to a type-erased implementation.
getNonTrivialCallbacks()->MovePtr(getInlineStorage(),
RHS.getInlineStorage());
getNonTrivialCallbacks()->DestroyPtr(RHS.getInlineStorage());
}
// Clear the old callback and inline flag to get back to as-if-null.

View File

@@ -1,688 +0,0 @@
//===-- llvm/ADT/Hashing.h - Utilities for hashing --------------*- 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 the newly proposed standard C++ interfaces for hashing
// arbitrary data and building hash functions for user-defined types. This
// interface was originally proposed in N3333[1] and is currently under review
// for inclusion in a future TR and/or standard.
//
// The primary interfaces provide are comprised of one type and three functions:
//
// -- 'hash_code' class is an opaque type representing the hash code for some
// data. It is the intended product of hashing, and can be used to implement
// hash tables, checksumming, and other common uses of hashes. It is not an
// integer type (although it can be converted to one) because it is risky
// to assume much about the internals of a hash_code. In particular, each
// execution of the program has a high probability of producing a different
// hash_code for a given input. Thus their values are not stable to save or
// persist, and should only be used during the execution for the
// construction of hashing datastructures.
//
// -- 'hash_value' is a function designed to be overloaded for each
// user-defined type which wishes to be used within a hashing context. It
// should be overloaded within the user-defined type's namespace and found
// via ADL. Overloads for primitive types are provided by this library.
//
// -- 'hash_combine' and 'hash_combine_range' are functions designed to aid
// programmers in easily and intuitively combining a set of data into
// a single hash_code for their object. They should only logically be used
// within the implementation of a 'hash_value' routine or similar context.
//
// Note that 'hash_combine_range' contains very special logic for hashing
// a contiguous array of integers or pointers. This logic is *extremely* fast,
// on a modern Intel "Gainestown" Xeon (Nehalem uarch) @2.2 GHz, these were
// benchmarked at over 6.5 GiB/s for large keys, and <20 cycles/hash for keys
// under 32-bytes.
//
//===----------------------------------------------------------------------===//
#ifndef WPIUTIL_WPI_HASHING_H
#define WPIUTIL_WPI_HASHING_H
#include "wpi/ErrorHandling.h"
#include "wpi/SwapByteOrder.h"
#include "wpi/type_traits.h"
#include <algorithm>
#include <bit>
#include <cassert>
#include <cstring>
#include <optional>
#include <string>
#include <tuple>
#include <utility>
#ifdef _WIN32
#pragma warning(push)
#pragma warning(disable : 26495)
#endif
namespace wpi {
template <typename T, typename Enable> struct DenseMapInfo;
/// An opaque object representing a hash code.
///
/// This object represents the result of hashing some entity. It is intended to
/// be used to implement hashtables or other hashing-based data structures.
/// While it wraps and exposes a numeric value, this value should not be
/// trusted to be stable or predictable across processes or executions.
///
/// In order to obtain the hash_code for an object 'x':
/// \code
/// using wpi::hash_value;
/// wpi::hash_code code = hash_value(x);
/// \endcode
class hash_code {
size_t value;
public:
/// Default construct a hash_code.
/// Note that this leaves the value uninitialized.
hash_code() = default;
/// Form a hash code directly from a numerical value.
hash_code(size_t value) : value(value) {}
/// Convert the hash code to its numerical value for use.
/*explicit*/ operator size_t() const { return value; }
friend bool operator==(const hash_code &lhs, const hash_code &rhs) {
return lhs.value == rhs.value;
}
friend bool operator!=(const hash_code &lhs, const hash_code &rhs) {
return lhs.value != rhs.value;
}
/// Allow a hash_code to be directly run through hash_value.
friend size_t hash_value(const hash_code &code) { return code.value; }
};
/// Compute a hash_code for any integer value.
///
/// Note that this function is intended to compute the same hash_code for
/// a particular value without regard to the pre-promotion type. This is in
/// contrast to hash_combine which may produce different hash_codes for
/// differing argument types even if they would implicit promote to a common
/// type without changing the value.
template <typename T>
std::enable_if_t<is_integral_or_enum<T>::value, hash_code> hash_value(T value);
/// Compute a hash_code for a pointer's address.
///
/// N.B.: This hashes the *address*. Not the value and not the type.
template <typename T> hash_code hash_value(const T *ptr);
/// Compute a hash_code for a pair of objects.
template <typename T, typename U>
hash_code hash_value(const std::pair<T, U> &arg);
/// Compute a hash_code for a tuple.
template <typename... Ts>
hash_code hash_value(const std::tuple<Ts...> &arg);
/// Compute a hash_code for a standard string.
template <typename T>
hash_code hash_value(const std::basic_string<T> &arg);
/// Compute a hash_code for a standard string.
template <typename T> hash_code hash_value(const std::optional<T> &arg);
// All of the implementation details of actually computing the various hash
// code values are held within this namespace. These routines are included in
// the header file mainly to allow inlining and constant propagation.
namespace hashing {
namespace detail {
inline uint64_t fetch64(const char *p) {
uint64_t result;
memcpy(&result, p, sizeof(result));
if (sys::IsBigEndianHost)
sys::swapByteOrder(result);
return result;
}
inline uint32_t fetch32(const char *p) {
uint32_t result;
memcpy(&result, p, sizeof(result));
if (sys::IsBigEndianHost)
sys::swapByteOrder(result);
return result;
}
/// Some primes between 2^63 and 2^64 for various uses.
static constexpr uint64_t k0 = 0xc3a5c85c97cb3127ULL;
static constexpr uint64_t k1 = 0xb492b66fbe98f273ULL;
static constexpr uint64_t k2 = 0x9ae16a3b2f90404fULL;
static constexpr uint64_t k3 = 0xc949d7c7509e6557ULL;
/// Bitwise right rotate.
/// Normally this will compile to a single instruction, especially if the
/// shift is a manifest constant.
inline uint64_t rotate(uint64_t val, size_t shift) {
// Avoid shifting by 64: doing so yields an undefined result.
return shift == 0 ? val : ((val >> shift) | (val << (64 - shift)));
}
inline uint64_t shift_mix(uint64_t val) {
return val ^ (val >> 47);
}
inline uint64_t hash_16_bytes(uint64_t low, uint64_t high) {
// Murmur-inspired hashing.
const uint64_t kMul = 0x9ddfea08eb382d69ULL;
uint64_t a = (low ^ high) * kMul;
a ^= (a >> 47);
uint64_t b = (high ^ a) * kMul;
b ^= (b >> 47);
b *= kMul;
return b;
}
inline uint64_t hash_1to3_bytes(const char *s, size_t len, uint64_t seed) {
uint8_t a = s[0];
uint8_t b = s[len >> 1];
uint8_t c = s[len - 1];
uint32_t y = static_cast<uint32_t>(a) + (static_cast<uint32_t>(b) << 8);
uint32_t z = static_cast<uint32_t>(len) + (static_cast<uint32_t>(c) << 2);
return shift_mix(y * k2 ^ z * k3 ^ seed) * k2;
}
inline uint64_t hash_4to8_bytes(const char *s, size_t len, uint64_t seed) {
uint64_t a = fetch32(s);
return hash_16_bytes(len + (a << 3), seed ^ fetch32(s + len - 4));
}
inline uint64_t hash_9to16_bytes(const char *s, size_t len, uint64_t seed) {
uint64_t a = fetch64(s);
uint64_t b = fetch64(s + len - 8);
return hash_16_bytes(seed ^ a, rotate(b + len, len)) ^ b;
}
inline uint64_t hash_17to32_bytes(const char *s, size_t len, uint64_t seed) {
uint64_t a = fetch64(s) * k1;
uint64_t b = fetch64(s + 8);
uint64_t c = fetch64(s + len - 8) * k2;
uint64_t d = fetch64(s + len - 16) * k0;
return hash_16_bytes(std::rotr<uint64_t>(a - b, 43) +
std::rotr<uint64_t>(c ^ seed, 30) + d,
a + std::rotr<uint64_t>(b ^ k3, 20) - c + len + seed);
}
inline uint64_t hash_33to64_bytes(const char *s, size_t len, uint64_t seed) {
uint64_t z = fetch64(s + 24);
uint64_t a = fetch64(s) + (len + fetch64(s + len - 16)) * k0;
uint64_t b = std::rotr<uint64_t>(a + z, 52);
uint64_t c = std::rotr<uint64_t>(a, 37);
a += fetch64(s + 8);
c += std::rotr<uint64_t>(a, 7);
a += fetch64(s + 16);
uint64_t vf = a + z;
uint64_t vs = b + std::rotr<uint64_t>(a, 31) + c;
a = fetch64(s + 16) + fetch64(s + len - 32);
z = fetch64(s + len - 8);
b = std::rotr<uint64_t>(a + z, 52);
c = std::rotr<uint64_t>(a, 37);
a += fetch64(s + len - 24);
c += std::rotr<uint64_t>(a, 7);
a += fetch64(s + len - 16);
uint64_t wf = a + z;
uint64_t ws = b + std::rotr<uint64_t>(a, 31) + c;
uint64_t r = shift_mix((vf + ws) * k2 + (wf + vs) * k0);
return shift_mix((seed ^ (r * k0)) + vs) * k2;
}
inline uint64_t hash_short(const char *s, size_t length, uint64_t seed) {
if (length >= 4 && length <= 8)
return hash_4to8_bytes(s, length, seed);
if (length > 8 && length <= 16)
return hash_9to16_bytes(s, length, seed);
if (length > 16 && length <= 32)
return hash_17to32_bytes(s, length, seed);
if (length > 32)
return hash_33to64_bytes(s, length, seed);
if (length != 0)
return hash_1to3_bytes(s, length, seed);
return k2 ^ seed;
}
/// The intermediate state used during hashing.
/// Currently, the algorithm for computing hash codes is based on CityHash and
/// keeps 56 bytes of arbitrary state.
struct hash_state {
uint64_t h0 = 0, h1 = 0, h2 = 0, h3 = 0, h4 = 0, h5 = 0, h6 = 0;
/// Create a new hash_state structure and initialize it based on the
/// seed and the first 64-byte chunk.
/// This effectively performs the initial mix.
static hash_state create(const char *s, uint64_t seed) {
hash_state state = {0,
seed,
hash_16_bytes(seed, k1),
std::rotr<uint64_t>(seed ^ k1, 49),
seed * k1,
shift_mix(seed),
0};
state.h6 = hash_16_bytes(state.h4, state.h5);
state.mix(s);
return state;
}
/// Mix 32-bytes from the input sequence into the 16-bytes of 'a'
/// and 'b', including whatever is already in 'a' and 'b'.
static void mix_32_bytes(const char *s, uint64_t &a, uint64_t &b) {
a += fetch64(s);
uint64_t c = fetch64(s + 24);
b = std::rotr<uint64_t>(b + a + c, 21);
uint64_t d = a;
a += fetch64(s + 8) + fetch64(s + 16);
b += std::rotr<uint64_t>(a, 44) + d;
a += c;
}
/// Mix in a 64-byte buffer of data.
/// We mix all 64 bytes even when the chunk length is smaller, but we
/// record the actual length.
void mix(const char *s) {
h0 = std::rotr<uint64_t>(h0 + h1 + h3 + fetch64(s + 8), 37) * k1;
h1 = std::rotr<uint64_t>(h1 + h4 + fetch64(s + 48), 42) * k1;
h0 ^= h6;
h1 += h3 + fetch64(s + 40);
h2 = std::rotr<uint64_t>(h2 + h5, 33) * k1;
h3 = h4 * k1;
h4 = h0 + h5;
mix_32_bytes(s, h3, h4);
h5 = h2 + h6;
h6 = h1 + fetch64(s + 16);
mix_32_bytes(s + 32, h5, h6);
std::swap(h2, h0);
}
/// Compute the final 64-bit hash code value based on the current
/// state and the length of bytes hashed.
uint64_t finalize(size_t length) {
return hash_16_bytes(hash_16_bytes(h3, h5) + shift_mix(h1) * k1 + h2,
hash_16_bytes(h4, h6) + shift_mix(length) * k1 + h0);
}
};
/// In LLVM_ENABLE_ABI_BREAKING_CHECKS builds, the seed is non-deterministic
/// per process (address of a function in LLVMSupport) to prevent having users
/// depend on the particular hash values. On platforms without ASLR, this is
/// still likely non-deterministic per build.
inline uint64_t get_execution_seed() {
// Work around x86-64 negative offset folding for old Clang -fno-pic
// https://reviews.llvm.org/D93931
#if LLVM_ENABLE_ABI_BREAKING_CHECKS && \
(!defined(__clang__) || __clang_major__ > 11)
return static_cast<uint64_t>(
reinterpret_cast<uintptr_t>(&install_fatal_error_handler));
#else
return 0xff51afd7ed558ccdULL;
#endif
}
/// Trait to indicate whether a type's bits can be hashed directly.
///
/// A type trait which is true if we want to combine values for hashing by
/// reading the underlying data. It is false if values of this type must
/// first be passed to hash_value, and the resulting hash_codes combined.
//
// FIXME: We want to replace is_integral_or_enum and is_pointer here with
// a predicate which asserts that comparing the underlying storage of two
// values of the type for equality is equivalent to comparing the two values
// for equality. For all the platforms we care about, this holds for integers
// and pointers, but there are platforms where it doesn't and we would like to
// support user-defined types which happen to satisfy this property.
template <typename T> struct is_hashable_data
: std::integral_constant<bool, ((is_integral_or_enum<T>::value ||
std::is_pointer<T>::value) &&
64 % sizeof(T) == 0)> {};
// Special case std::pair to detect when both types are viable and when there
// is no alignment-derived padding in the pair. This is a bit of a lie because
// std::pair isn't truly POD, but it's close enough in all reasonable
// implementations for our use case of hashing the underlying data.
template <typename T, typename U> struct is_hashable_data<std::pair<T, U> >
: std::integral_constant<bool, (is_hashable_data<T>::value &&
is_hashable_data<U>::value &&
(sizeof(T) + sizeof(U)) ==
sizeof(std::pair<T, U>))> {};
/// Helper to get the hashable data representation for a type.
/// This variant is enabled when the type itself can be used.
template <typename T>
std::enable_if_t<is_hashable_data<T>::value, T>
get_hashable_data(const T &value) {
return value;
}
/// Helper to get the hashable data representation for a type.
/// This variant is enabled when we must first call hash_value and use the
/// result as our data.
template <typename T>
std::enable_if_t<!is_hashable_data<T>::value, size_t>
get_hashable_data(const T &value) {
using ::wpi::hash_value;
return hash_value(value);
}
/// Helper to store data from a value into a buffer and advance the
/// pointer into that buffer.
///
/// This routine first checks whether there is enough space in the provided
/// buffer, and if not immediately returns false. If there is space, it
/// copies the underlying bytes of value into the buffer, advances the
/// buffer_ptr past the copied bytes, and returns true.
template <typename T>
bool store_and_advance(char *&buffer_ptr, char *buffer_end, const T& value,
size_t offset = 0) {
size_t store_size = sizeof(value) - offset;
if (buffer_ptr + store_size > buffer_end)
return false;
const char *value_data = reinterpret_cast<const char *>(&value);
memcpy(buffer_ptr, value_data + offset, store_size);
buffer_ptr += store_size;
return true;
}
/// Implement the combining of integral values into a hash_code.
///
/// This overload is selected when the value type of the iterator is
/// integral. Rather than computing a hash_code for each object and then
/// combining them, this (as an optimization) directly combines the integers.
template <typename InputIteratorT>
hash_code hash_combine_range_impl(InputIteratorT first, InputIteratorT last) {
const uint64_t seed = get_execution_seed();
char buffer[64], *buffer_ptr = buffer;
char *const buffer_end = std::end(buffer);
while (first != last && store_and_advance(buffer_ptr, buffer_end,
get_hashable_data(*first)))
++first;
if (first == last)
return hash_short(buffer, buffer_ptr - buffer, seed);
assert(buffer_ptr == buffer_end);
hash_state state = state.create(buffer, seed);
size_t length = 64;
while (first != last) {
// Fill up the buffer. We don't clear it, which re-mixes the last round
// when only a partial 64-byte chunk is left.
buffer_ptr = buffer;
while (first != last && store_and_advance(buffer_ptr, buffer_end,
get_hashable_data(*first)))
++first;
// Rotate the buffer if we did a partial fill in order to simulate doing
// a mix of the last 64-bytes. That is how the algorithm works when we
// have a contiguous byte sequence, and we want to emulate that here.
std::rotate(buffer, buffer_ptr, buffer_end);
// Mix this chunk into the current state.
state.mix(buffer);
length += buffer_ptr - buffer;
};
return state.finalize(length);
}
/// Implement the combining of integral values into a hash_code.
///
/// This overload is selected when the value type of the iterator is integral
/// and when the input iterator is actually a pointer. Rather than computing
/// a hash_code for each object and then combining them, this (as an
/// optimization) directly combines the integers. Also, because the integers
/// are stored in contiguous memory, this routine avoids copying each value
/// and directly reads from the underlying memory.
template <typename ValueT>
std::enable_if_t<is_hashable_data<ValueT>::value, hash_code>
hash_combine_range_impl(ValueT *first, ValueT *last) {
const uint64_t seed = get_execution_seed();
const char *s_begin = reinterpret_cast<const char *>(first);
const char *s_end = reinterpret_cast<const char *>(last);
const size_t length = std::distance(s_begin, s_end);
if (length <= 64)
return hash_short(s_begin, length, seed);
const char *s_aligned_end = s_begin + (length & ~63);
hash_state state = state.create(s_begin, seed);
s_begin += 64;
while (s_begin != s_aligned_end) {
state.mix(s_begin);
s_begin += 64;
}
if (length & 63)
state.mix(s_end - 64);
return state.finalize(length);
}
} // namespace detail
} // namespace hashing
/// Compute a hash_code for a sequence of values.
///
/// This hashes a sequence of values. It produces the same hash_code as
/// 'hash_combine(a, b, c, ...)', but can run over arbitrary sized sequences
/// and is significantly faster given pointers and types which can be hashed as
/// a sequence of bytes.
template <typename InputIteratorT>
hash_code hash_combine_range(InputIteratorT first, InputIteratorT last) {
return ::wpi::hashing::detail::hash_combine_range_impl(first, last);
}
// Implementation details for hash_combine.
namespace hashing {
namespace detail {
/// Helper class to manage the recursive combining of hash_combine
/// arguments.
///
/// This class exists to manage the state and various calls involved in the
/// recursive combining of arguments used in hash_combine. It is particularly
/// useful at minimizing the code in the recursive calls to ease the pain
/// caused by a lack of variadic functions.
struct hash_combine_recursive_helper {
char buffer[64] = {};
hash_state state;
const uint64_t seed;
public:
/// Construct a recursive hash combining helper.
///
/// This sets up the state for a recursive hash combine, including getting
/// the seed and buffer setup.
hash_combine_recursive_helper()
: seed(get_execution_seed()) {}
/// Combine one chunk of data into the current in-flight hash.
///
/// This merges one chunk of data into the hash. First it tries to buffer
/// the data. If the buffer is full, it hashes the buffer into its
/// hash_state, empties it, and then merges the new chunk in. This also
/// handles cases where the data straddles the end of the buffer.
template <typename T>
char *combine_data(size_t &length, char *buffer_ptr, char *buffer_end, T data) {
if (!store_and_advance(buffer_ptr, buffer_end, data)) {
// Check for skew which prevents the buffer from being packed, and do
// a partial store into the buffer to fill it. This is only a concern
// with the variadic combine because that formation can have varying
// argument types.
size_t partial_store_size = buffer_end - buffer_ptr;
memcpy(buffer_ptr, &data, partial_store_size);
// If the store fails, our buffer is full and ready to hash. We have to
// either initialize the hash state (on the first full buffer) or mix
// this buffer into the existing hash state. Length tracks the *hashed*
// length, not the buffered length.
if (length == 0) {
state = state.create(buffer, seed);
length = 64;
} else {
// Mix this chunk into the current state and bump length up by 64.
state.mix(buffer);
length += 64;
}
// Reset the buffer_ptr to the head of the buffer for the next chunk of
// data.
buffer_ptr = buffer;
// Try again to store into the buffer -- this cannot fail as we only
// store types smaller than the buffer.
if (!store_and_advance(buffer_ptr, buffer_end, data,
partial_store_size))
wpi_unreachable("buffer smaller than stored type");
}
return buffer_ptr;
}
/// Recursive, variadic combining method.
///
/// This function recurses through each argument, combining that argument
/// into a single hash.
template <typename T, typename ...Ts>
hash_code combine(size_t length, char *buffer_ptr, char *buffer_end,
const T &arg, const Ts &...args) {
buffer_ptr = combine_data(length, buffer_ptr, buffer_end, get_hashable_data(arg));
// Recurse to the next argument.
return combine(length, buffer_ptr, buffer_end, args...);
}
/// Base case for recursive, variadic combining.
///
/// The base case when combining arguments recursively is reached when all
/// arguments have been handled. It flushes the remaining buffer and
/// constructs a hash_code.
hash_code combine(size_t length, char *buffer_ptr, char *buffer_end) {
// Check whether the entire set of values fit in the buffer. If so, we'll
// use the optimized short hashing routine and skip state entirely.
if (length == 0)
return hash_short(buffer, buffer_ptr - buffer, seed);
// Mix the final buffer, rotating it if we did a partial fill in order to
// simulate doing a mix of the last 64-bytes. That is how the algorithm
// works when we have a contiguous byte sequence, and we want to emulate
// that here.
std::rotate(buffer, buffer_ptr, buffer_end);
// Mix this chunk into the current state.
state.mix(buffer);
length += buffer_ptr - buffer;
return state.finalize(length);
}
};
} // namespace detail
} // namespace hashing
/// Combine values into a single hash_code.
///
/// This routine accepts a varying number of arguments of any type. It will
/// attempt to combine them into a single hash_code. For user-defined types it
/// attempts to call a \see hash_value overload (via ADL) for the type. For
/// integer and pointer types it directly combines their data into the
/// resulting hash_code.
///
/// The result is suitable for returning from a user's hash_value
/// *implementation* for their user-defined type. Consumers of a type should
/// *not* call this routine, they should instead call 'hash_value'.
template <typename ...Ts> hash_code hash_combine(const Ts &...args) {
// Recursively hash each argument using a helper class.
::wpi::hashing::detail::hash_combine_recursive_helper helper;
return helper.combine(0, helper.buffer, helper.buffer + 64, args...);
}
// Implementation details for implementations of hash_value overloads provided
// here.
namespace hashing {
namespace detail {
/// Helper to hash the value of a single integer.
///
/// Overloads for smaller integer types are not provided to ensure consistent
/// behavior in the presence of integral promotions. Essentially,
/// "hash_value('4')" and "hash_value('0' + 4)" should be the same.
inline hash_code hash_integer_value(uint64_t value) {
// Similar to hash_4to8_bytes but using a seed instead of length.
const uint64_t seed = get_execution_seed();
const char *s = reinterpret_cast<const char *>(&value);
const uint64_t a = fetch32(s);
return hash_16_bytes(seed + (a << 3), fetch32(s + 4));
}
} // namespace detail
} // namespace hashing
// Declared and documented above, but defined here so that any of the hashing
// infrastructure is available.
template <typename T>
std::enable_if_t<is_integral_or_enum<T>::value, hash_code> hash_value(T value) {
return ::wpi::hashing::detail::hash_integer_value(
static_cast<uint64_t>(value));
}
// Declared and documented above, but defined here so that any of the hashing
// infrastructure is available.
template <typename T> hash_code hash_value(const T *ptr) {
return ::wpi::hashing::detail::hash_integer_value(
reinterpret_cast<uintptr_t>(ptr));
}
// Declared and documented above, but defined here so that any of the hashing
// infrastructure is available.
template <typename T, typename U>
hash_code hash_value(const std::pair<T, U> &arg) {
return hash_combine(arg.first, arg.second);
}
template <typename... Ts> hash_code hash_value(const std::tuple<Ts...> &arg) {
return std::apply([](const auto &...xs) { return hash_combine(xs...); }, arg);
}
// Declared and documented above, but defined here so that any of the hashing
// infrastructure is available.
template <typename T>
hash_code hash_value(const std::basic_string<T> &arg) {
return hash_combine_range(arg.begin(), arg.end());
}
template <typename T> hash_code hash_value(const std::optional<T> &arg) {
return arg ? hash_combine(true, *arg) : hash_value(false);
}
template <> struct DenseMapInfo<hash_code, void> {
static inline hash_code getEmptyKey() { return hash_code(-1); }
static inline hash_code getTombstoneKey() { return hash_code(-2); }
static unsigned getHashValue(hash_code val) {
return static_cast<unsigned>(size_t(val));
}
static bool isEqual(hash_code LHS, hash_code RHS) { return LHS == RHS; }
};
} // namespace wpi
/// Implement std::hash so that hash_code can be used in STL containers.
namespace std {
template<>
struct hash<wpi::hash_code> {
size_t operator()(wpi::hash_code const& Val) const {
return Val;
}
};
} // namespace std;
#ifdef _WIN32
#pragma warning(pop)
#endif
#endif

View File

@@ -1,259 +0,0 @@
//===- llvm/ADT/MapVector.h - Map w/ deterministic value order --*- 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
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file implements a map that provides insertion order iteration. The
/// interface is purposefully minimal. The key is assumed to be cheap to copy
/// and 2 copies are kept, one for indexing in a DenseMap, one for iteration in
/// a SmallVector.
///
//===----------------------------------------------------------------------===//
#ifndef WPIUTIL_WPI_MAPVECTOR_H
#define WPIUTIL_WPI_MAPVECTOR_H
#include "wpi/DenseMap.h"
#include "wpi/SmallVector.h"
#include <cassert>
#include <cstddef>
#include <iterator>
#include <type_traits>
#include <utility>
namespace wpi {
/// This class implements a map that also provides access to all stored values
/// in a deterministic order. The values are kept in a SmallVector<*, 0> and the
/// mapping is done with DenseMap from Keys to indexes in that vector.
template <typename KeyT, typename ValueT,
typename MapType = DenseMap<KeyT, unsigned>,
typename VectorType = SmallVector<std::pair<KeyT, ValueT>, 0>>
class MapVector {
MapType Map;
VectorType Vector;
static_assert(
std::is_integral_v<typename MapType::mapped_type>,
"The mapped_type of the specified Map must be an integral type");
public:
using key_type = KeyT;
using value_type = typename VectorType::value_type;
using size_type = typename VectorType::size_type;
using iterator = typename VectorType::iterator;
using const_iterator = typename VectorType::const_iterator;
using reverse_iterator = typename VectorType::reverse_iterator;
using const_reverse_iterator = typename VectorType::const_reverse_iterator;
/// Clear the MapVector and return the underlying vector.
VectorType takeVector() {
Map.clear();
return std::move(Vector);
}
size_type size() const { return Vector.size(); }
/// Grow the MapVector so that it can contain at least \p NumEntries items
/// before resizing again.
void reserve(size_type NumEntries) {
Map.reserve(NumEntries);
Vector.reserve(NumEntries);
}
iterator begin() { return Vector.begin(); }
const_iterator begin() const { return Vector.begin(); }
iterator end() { return Vector.end(); }
const_iterator end() const { return Vector.end(); }
reverse_iterator rbegin() { return Vector.rbegin(); }
const_reverse_iterator rbegin() const { return Vector.rbegin(); }
reverse_iterator rend() { return Vector.rend(); }
const_reverse_iterator rend() const { return Vector.rend(); }
bool empty() const {
return Vector.empty();
}
std::pair<KeyT, ValueT> &front() { return Vector.front(); }
const std::pair<KeyT, ValueT> &front() const { return Vector.front(); }
std::pair<KeyT, ValueT> &back() { return Vector.back(); }
const std::pair<KeyT, ValueT> &back() const { return Vector.back(); }
void clear() {
Map.clear();
Vector.clear();
}
void swap(MapVector &RHS) {
std::swap(Map, RHS.Map);
std::swap(Vector, RHS.Vector);
}
ValueT &operator[](const KeyT &Key) {
std::pair<KeyT, typename MapType::mapped_type> Pair = std::make_pair(Key, 0);
std::pair<typename MapType::iterator, bool> Result = Map.insert(Pair);
auto &I = Result.first->second;
if (Result.second) {
Vector.push_back(std::make_pair(Key, ValueT()));
I = Vector.size() - 1;
}
return Vector[I].second;
}
// Returns a copy of the value. Only allowed if ValueT is copyable.
ValueT lookup(const KeyT &Key) const {
static_assert(std::is_copy_constructible_v<ValueT>,
"Cannot call lookup() if ValueT is not copyable.");
typename MapType::const_iterator Pos = Map.find(Key);
return Pos == Map.end()? ValueT() : Vector[Pos->second].second;
}
template <typename... Ts>
std::pair<iterator, bool> try_emplace(const KeyT &Key, Ts &&...Args) {
auto [It, Inserted] = Map.insert(std::make_pair(Key, 0));
if (Inserted) {
It->second = Vector.size();
Vector.emplace_back(std::piecewise_construct, std::forward_as_tuple(Key),
std::forward_as_tuple(std::forward<Ts>(Args)...));
return std::make_pair(std::prev(end()), true);
}
return std::make_pair(begin() + It->second, false);
}
template <typename... Ts>
std::pair<iterator, bool> try_emplace(KeyT &&Key, Ts &&...Args) {
auto [It, Inserted] = Map.insert(std::make_pair(Key, 0));
if (Inserted) {
It->second = Vector.size();
Vector.emplace_back(std::piecewise_construct,
std::forward_as_tuple(std::move(Key)),
std::forward_as_tuple(std::forward<Ts>(Args)...));
return std::make_pair(std::prev(end()), true);
}
return std::make_pair(begin() + It->second, false);
}
std::pair<iterator, bool> insert(const std::pair<KeyT, ValueT> &KV) {
return try_emplace(KV.first, KV.second);
}
std::pair<iterator, bool> insert(std::pair<KeyT, ValueT> &&KV) {
return try_emplace(std::move(KV.first), std::move(KV.second));
}
template <typename V>
std::pair<iterator, bool> insert_or_assign(const KeyT &Key, V &&Val) {
auto Ret = try_emplace(Key, std::forward<V>(Val));
if (!Ret.second)
Ret.first->second = std::forward<V>(Val);
return Ret;
}
template <typename V>
std::pair<iterator, bool> insert_or_assign(KeyT &&Key, V &&Val) {
auto Ret = try_emplace(std::move(Key), std::forward<V>(Val));
if (!Ret.second)
Ret.first->second = std::forward<V>(Val);
return Ret;
}
bool contains(const KeyT &Key) const { return Map.find(Key) != Map.end(); }
size_type count(const KeyT &Key) const { return contains(Key) ? 1 : 0; }
iterator find(const KeyT &Key) {
typename MapType::const_iterator Pos = Map.find(Key);
return Pos == Map.end()? Vector.end() :
(Vector.begin() + Pos->second);
}
const_iterator find(const KeyT &Key) const {
typename MapType::const_iterator Pos = Map.find(Key);
return Pos == Map.end()? Vector.end() :
(Vector.begin() + Pos->second);
}
/// Remove the last element from the vector.
void pop_back() {
typename MapType::iterator Pos = Map.find(Vector.back().first);
Map.erase(Pos);
Vector.pop_back();
}
/// Remove the element given by Iterator.
///
/// Returns an iterator to the element following the one which was removed,
/// which may be end().
///
/// \note This is a deceivingly expensive operation (linear time). It's
/// usually better to use \a remove_if() if possible.
typename VectorType::iterator erase(typename VectorType::iterator Iterator) {
Map.erase(Iterator->first);
auto Next = Vector.erase(Iterator);
if (Next == Vector.end())
return Next;
// Update indices in the map.
size_t Index = Next - Vector.begin();
for (auto &I : Map) {
assert(I.second != Index && "Index was already erased!");
if (I.second > Index)
--I.second;
}
return Next;
}
/// Remove all elements with the key value Key.
///
/// Returns the number of elements removed.
size_type erase(const KeyT &Key) {
auto Iterator = find(Key);
if (Iterator == end())
return 0;
erase(Iterator);
return 1;
}
/// Remove the elements that match the predicate.
///
/// Erase all elements that match \c Pred in a single pass. Takes linear
/// time.
template <class Predicate> void remove_if(Predicate Pred);
};
template <typename KeyT, typename ValueT, typename MapType, typename VectorType>
template <class Function>
void MapVector<KeyT, ValueT, MapType, VectorType>::remove_if(Function Pred) {
auto O = Vector.begin();
for (auto I = O, E = Vector.end(); I != E; ++I) {
if (Pred(*I)) {
// Erase from the map.
Map.erase(I->first);
continue;
}
if (I != O) {
// Move the value and update the index in the map.
*O = std::move(*I);
Map[O->first] = O - Vector.begin();
}
++O;
}
// Erase trailing entries in the vector.
Vector.erase(O, Vector.end());
}
/// A MapVector that performs no allocations if smaller than a certain
/// size.
template <typename KeyT, typename ValueT, unsigned N>
struct SmallMapVector
: MapVector<KeyT, ValueT, SmallDenseMap<KeyT, unsigned, N>,
SmallVector<std::pair<KeyT, ValueT>, N>> {
};
} // end namespace wpi
#endif // WPIUTIL_WPI_MAPVECTOR_H

View File

@@ -471,13 +471,21 @@ constexpr uint64_t alignTo(uint64_t Value, uint64_t Align) {
return CeilDiv * Align;
}
/// Will overflow only if result is not representable in T.
template <typename U, typename V, typename T = common_uint<U, V>>
constexpr T alignToPowerOf2(U Value, V Align) {
assert(Align != 0 && (Align & (Align - 1)) == 0 &&
"Align must be a power of 2");
T NegAlign = static_cast<T>(0) - Align;
return (Value + (Align - 1)) & NegAlign;
}
/// Fallback when arguments aren't integral.
constexpr uint64_t alignToPowerOf2(uint64_t Value, uint64_t Align) {
assert(Align != 0 && (Align & (Align - 1)) == 0 &&
"Align must be a power of 2");
// Replace unary minus to avoid compilation error on Windows:
// "unary minus operator applied to unsigned type, result still unsigned"
uint64_t NegAlign = (~Align) + 1;
return (Value + Align - 1) & NegAlign;
uint64_t NegAlign = 0 - Align;
return (Value + (Align - 1)) & NegAlign;
}
/// If non-zero \p Skew is specified, the return value will be a minimal integer

View File

@@ -192,12 +192,18 @@ public:
// isa<T>, cast<T> and the wpi::dyn_cast<T>
/// Test if the Union currently holds the type matching T.
template <typename T> inline bool is() const { return isa<T>(*this); }
template <typename T>
[[deprecated("Use isa instead")]]
inline bool is() const {
return isa<T>(*this);
}
/// Returns the value of the specified pointer type.
///
/// If the specified pointer type is incorrect, assert.
template <typename T> inline T get() const {
template <typename T>
[[deprecated("Use cast instead")]]
inline T get() const {
assert(isa<T>(*this) && "Invalid accessor called");
return cast<T>(*this);
}

View File

@@ -16,9 +16,10 @@
#define WPIUTIL_WPI_SMALLPTRSET_H
#include "wpi/EpochTracker.h"
#include "wpi/Compiler.h"
#include "wpi/MathExtras.h"
#include "wpi/ReverseIteration.h"
#include "wpi/type_traits.h"
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdlib>
@@ -52,10 +53,7 @@ class SmallPtrSetImplBase : public DebugEpochBase {
friend class SmallPtrSetIteratorImpl;
protected:
/// SmallArray - Points to a fixed size set of buckets, used in 'small mode'.
const void **SmallArray;
/// CurArray - This is the current set of buckets. If equal to SmallArray,
/// then the set is in 'small mode'.
/// The current set of buckets, in either small or big representation.
const void **CurArray;
/// CurArraySize - The allocated size of CurArray, always a power of two.
unsigned CurArraySize;
@@ -66,16 +64,18 @@ protected:
unsigned NumNonEmpty;
/// Number of tombstones in CurArray.
unsigned NumTombstones;
/// Whether the set is in small representation.
bool IsSmall;
// Helpers to copy and move construct a SmallPtrSet.
SmallPtrSetImplBase(const void **SmallStorage,
const SmallPtrSetImplBase &that);
SmallPtrSetImplBase(const void **SmallStorage, unsigned SmallSize,
SmallPtrSetImplBase &&that);
const void **RHSSmallStorage, SmallPtrSetImplBase &&that);
explicit SmallPtrSetImplBase(const void **SmallStorage, unsigned SmallSize)
: SmallArray(SmallStorage), CurArray(SmallStorage),
CurArraySize(SmallSize), NumNonEmpty(0), NumTombstones(0) {
: CurArray(SmallStorage), CurArraySize(SmallSize), NumNonEmpty(0),
NumTombstones(0), IsSmall(true) {
assert(SmallSize && (SmallSize & (SmallSize-1)) == 0 &&
"Initial size must be a power of two!");
}
@@ -92,6 +92,7 @@ public:
[[nodiscard]] bool empty() const { return size() == 0; }
size_type size() const { return NumNonEmpty - NumTombstones; }
size_type capacity() const { return CurArraySize; }
void clear() {
incrementEpoch();
@@ -108,6 +109,27 @@ public:
NumTombstones = 0;
}
void reserve(size_type NumEntries) {
incrementEpoch();
// Do nothing if we're given zero as a reservation size.
if (NumEntries == 0)
return;
// No need to expand if we're small and NumEntries will fit in the space.
if (isSmall() && NumEntries <= CurArraySize)
return;
// insert_imp_big will reallocate if stores is more than 75% full, on the
// /final/ insertion.
if (!isSmall() && ((NumEntries - 1) * 4) < (CurArraySize * 3))
return;
// We must Grow -- find the size where we'd be 75% full, then round up to
// the next power of two.
size_type NewSize = NumEntries + (NumEntries / 3);
NewSize = 1 << (Log2_32_Ceil(NewSize) + 1);
// Like insert_imp_big, always allocate at least 128 elements.
NewSize = (std::max)(128u, NewSize);
Grow(NewSize);
}
protected:
static void *getTombstoneMarker() { return reinterpret_cast<void*>(-2); }
@@ -127,7 +149,7 @@ protected:
std::pair<const void *const *, bool> insert_imp(const void *Ptr) {
if (isSmall()) {
// Check to see if it is already in the set.
for (const void **APtr = SmallArray, **E = SmallArray + NumNonEmpty;
for (const void **APtr = CurArray, **E = CurArray + NumNonEmpty;
APtr != E; ++APtr) {
const void *Value = *APtr;
if (Value == Ptr)
@@ -136,9 +158,9 @@ protected:
// Nope, there isn't. If we stay small, just 'pushback' now.
if (NumNonEmpty < CurArraySize) {
SmallArray[NumNonEmpty++] = Ptr;
CurArray[NumNonEmpty++] = Ptr;
incrementEpoch();
return std::make_pair(SmallArray + (NumNonEmpty - 1), true);
return std::make_pair(CurArray + (NumNonEmpty - 1), true);
}
// Otherwise, hit the big set case, which will call grow.
}
@@ -151,10 +173,10 @@ protected:
/// in.
bool erase_imp(const void * Ptr) {
if (isSmall()) {
for (const void **APtr = SmallArray, **E = SmallArray + NumNonEmpty;
for (const void **APtr = CurArray, **E = CurArray + NumNonEmpty;
APtr != E; ++APtr) {
if (*APtr == Ptr) {
*APtr = SmallArray[--NumNonEmpty];
*APtr = CurArray[--NumNonEmpty];
incrementEpoch();
return true;
}
@@ -162,8 +184,8 @@ protected:
return false;
}
auto *Bucket = FindBucketFor(Ptr);
if (*Bucket != Ptr)
auto *Bucket = doFind(Ptr);
if (!Bucket)
return false;
*const_cast<const void **>(Bucket) = getTombstoneMarker();
@@ -180,25 +202,40 @@ protected:
const void *const * find_imp(const void * Ptr) const {
if (isSmall()) {
// Linear search for the item.
for (const void *const *APtr = SmallArray,
*const *E = SmallArray + NumNonEmpty; APtr != E; ++APtr)
for (const void *const *APtr = CurArray, *const *E =
CurArray + NumNonEmpty;
APtr != E; ++APtr)
if (*APtr == Ptr)
return APtr;
return EndPointer();
}
// Big set case.
auto *Bucket = FindBucketFor(Ptr);
if (*Bucket == Ptr)
if (auto *Bucket = doFind(Ptr))
return Bucket;
return EndPointer();
}
bool isSmall() const { return CurArray == SmallArray; }
bool contains_imp(const void *Ptr) const {
if (isSmall()) {
// Linear search for the item.
const void *const *APtr = CurArray;
const void *const *E = CurArray + NumNonEmpty;
for (; APtr != E; ++APtr)
if (*APtr == Ptr)
return true;
return false;
}
return doFind(Ptr) != nullptr;
}
bool isSmall() const { return IsSmall; }
private:
std::pair<const void *const *, bool> insert_imp_big(const void *Ptr);
const void *const *doFind(const void *Ptr) const;
const void * const *FindBucketFor(const void *Ptr) const;
void shrink_and_clear();
@@ -208,16 +245,19 @@ private:
protected:
/// swap - Swaps the elements of two sets.
/// Note: This method assumes that both sets have the same small size.
void swap(SmallPtrSetImplBase &RHS);
void swap(const void **SmallStorage, const void **RHSSmallStorage,
SmallPtrSetImplBase &RHS);
void CopyFrom(const SmallPtrSetImplBase &RHS);
void MoveFrom(unsigned SmallSize, SmallPtrSetImplBase &&RHS);
void copyFrom(const void **SmallStorage, const SmallPtrSetImplBase &RHS);
void moveFrom(const void **SmallStorage, unsigned SmallSize,
const void **RHSSmallStorage, SmallPtrSetImplBase &&RHS);
private:
/// Code shared by MoveFrom() and move constructor.
void MoveHelper(unsigned SmallSize, SmallPtrSetImplBase &&RHS);
/// Code shared by CopyFrom() and copy constructor.
void CopyHelper(const SmallPtrSetImplBase &RHS);
/// Code shared by moveFrom() and move constructor.
void moveHelper(const void **SmallStorage, unsigned SmallSize,
const void **RHSSmallStorage, SmallPtrSetImplBase &&RHS);
/// Code shared by copyFrom() and copy constructor.
void copyHelper(const SmallPtrSetImplBase &RHS);
};
/// SmallPtrSetIteratorImpl - This is the common base class shared between all
@@ -378,7 +418,7 @@ public:
bool remove_if(UnaryPredicate P) {
bool Removed = false;
if (isSmall()) {
const void **APtr = SmallArray, **E = SmallArray + NumNonEmpty;
const void **APtr = CurArray, **E = CurArray + NumNonEmpty;
while (APtr != E) {
PtrType Ptr = PtrTraits::getFromVoidPointer(const_cast<void *>(*APtr));
if (P(Ptr)) {
@@ -410,13 +450,13 @@ public:
/// count - Return 1 if the specified pointer is in the set, 0 otherwise.
size_type count(ConstPtrType Ptr) const {
return find_imp(ConstPtrTraits::getAsVoidPointer(Ptr)) != EndPointer();
return contains_imp(ConstPtrTraits::getAsVoidPointer(Ptr));
}
iterator find(ConstPtrType Ptr) const {
return makeIterator(find_imp(ConstPtrTraits::getAsVoidPointer(Ptr)));
}
bool contains(ConstPtrType Ptr) const {
return find_imp(ConstPtrTraits::getAsVoidPointer(Ptr)) != EndPointer();
return contains_imp(ConstPtrTraits::getAsVoidPointer(Ptr));
}
template <typename IterT>
@@ -503,7 +543,8 @@ public:
SmallPtrSet() : BaseT(SmallStorage, SmallSizePowTwo) {}
SmallPtrSet(const SmallPtrSet &that) : BaseT(SmallStorage, that) {}
SmallPtrSet(SmallPtrSet &&that)
: BaseT(SmallStorage, SmallSizePowTwo, std::move(that)) {}
: BaseT(SmallStorage, SmallSizePowTwo, that.SmallStorage,
std::move(that)) {}
template<typename It>
SmallPtrSet(It I, It E) : BaseT(SmallStorage, SmallSizePowTwo) {
@@ -518,14 +559,15 @@ public:
SmallPtrSet<PtrType, SmallSize> &
operator=(const SmallPtrSet<PtrType, SmallSize> &RHS) {
if (&RHS != this)
this->CopyFrom(RHS);
this->copyFrom(SmallStorage, RHS);
return *this;
}
SmallPtrSet<PtrType, SmallSize> &
operator=(SmallPtrSet<PtrType, SmallSize> &&RHS) {
if (&RHS != this)
this->MoveFrom(SmallSizePowTwo, std::move(RHS));
this->moveFrom(SmallStorage, SmallSizePowTwo, RHS.SmallStorage,
std::move(RHS));
return *this;
}
@@ -538,7 +580,7 @@ public:
/// swap - Swaps the elements of two sets.
void swap(SmallPtrSet<PtrType, SmallSize> &RHS) {
SmallPtrSetImplBase::swap(RHS);
SmallPtrSetImplBase::swap(SmallStorage, RHS.SmallStorage, RHS);
}
};

View File

@@ -17,13 +17,10 @@
#include "wpi/SmallPtrSet.h"
#include "wpi/SmallVector.h"
#include "wpi/iterator.h"
#include "wpi/Compiler.h"
#include "wpi/type_traits.h"
#include <cstddef>
#include <functional>
#include <optional>
#include <initializer_list>
#include <set>
#include <type_traits>
#include <utility>
namespace wpi {
@@ -46,24 +43,24 @@ private:
VecIterTy VecIter;
};
bool isSmall;
bool IsSmall;
public:
SmallSetIterator(SetIterTy SetIter) : SetIter(SetIter), isSmall(false) {}
SmallSetIterator(SetIterTy SetIter) : SetIter(SetIter), IsSmall(false) {}
SmallSetIterator(VecIterTy VecIter) : VecIter(VecIter), isSmall(true) {}
SmallSetIterator(VecIterTy VecIter) : VecIter(VecIter), IsSmall(true) {}
// Spell out destructor, copy/move constructor and assignment operators for
// MSVC STL, where set<T>::const_iterator is not trivially copy constructible.
~SmallSetIterator() {
if (isSmall)
if (IsSmall)
VecIter.~VecIterTy();
else
SetIter.~SetIterTy();
}
SmallSetIterator(const SmallSetIterator &Other) : isSmall(Other.isSmall) {
if (isSmall)
SmallSetIterator(const SmallSetIterator &Other) : IsSmall(Other.IsSmall) {
if (IsSmall)
VecIter = Other.VecIter;
else
// Use placement new, to make sure SetIter is properly constructed, even
@@ -71,8 +68,8 @@ public:
new (&SetIter) SetIterTy(Other.SetIter);
}
SmallSetIterator(SmallSetIterator &&Other) : isSmall(Other.isSmall) {
if (isSmall)
SmallSetIterator(SmallSetIterator &&Other) : IsSmall(Other.IsSmall) {
if (IsSmall)
VecIter = std::move(Other.VecIter);
else
// Use placement new, to make sure SetIter is properly constructed, even
@@ -83,11 +80,11 @@ public:
SmallSetIterator& operator=(const SmallSetIterator& Other) {
// Call destructor for SetIter, so it gets properly destroyed if it is
// not trivially destructible in case we are setting VecIter.
if (!isSmall)
if (!IsSmall)
SetIter.~SetIterTy();
isSmall = Other.isSmall;
if (isSmall)
IsSmall = Other.IsSmall;
if (IsSmall)
VecIter = Other.VecIter;
else
new (&SetIter) SetIterTy(Other.SetIter);
@@ -97,11 +94,11 @@ public:
SmallSetIterator& operator=(SmallSetIterator&& Other) {
// Call destructor for SetIter, so it gets properly destroyed if it is
// not trivially destructible in case we are setting VecIter.
if (!isSmall)
if (!IsSmall)
SetIter.~SetIterTy();
isSmall = Other.isSmall;
if (isSmall)
IsSmall = Other.IsSmall;
if (IsSmall)
VecIter = std::move(Other.VecIter);
else
new (&SetIter) SetIterTy(std::move(Other.SetIter));
@@ -109,22 +106,22 @@ public:
}
bool operator==(const SmallSetIterator &RHS) const {
if (isSmall != RHS.isSmall)
if (IsSmall != RHS.IsSmall)
return false;
if (isSmall)
if (IsSmall)
return VecIter == RHS.VecIter;
return SetIter == RHS.SetIter;
}
SmallSetIterator &operator++() { // Preincrement
if (isSmall)
VecIter++;
if (IsSmall)
++VecIter;
else
SetIter++;
++SetIter;
return *this;
}
const T &operator*() const { return isSmall ? *VecIter : *SetIter; }
const T &operator*() const { return IsSmall ? *VecIter : *SetIter; }
};
/// SmallSet - This maintains a set of unique values, optimizing for the case
@@ -139,10 +136,6 @@ class SmallSet {
SmallVector<T, N> Vector;
std::set<T, C> Set;
using VIterator = typename SmallVector<T, N>::const_iterator;
using SIterator = typename std::set<T, C>::const_iterator;
using mutable_iterator = typename SmallVector<T, N>::iterator;
// In small mode SmallPtrSet uses linear search for the elements, so it is
// not a good idea to choose this value too high. You may consider using a
// DenseSet<> instead if you expect many elements in the set.
@@ -155,6 +148,22 @@ public:
using const_iterator = SmallSetIterator<T, N, C>;
SmallSet() = default;
SmallSet(const SmallSet &) = default;
SmallSet(SmallSet &&) = default;
template <typename IterT> SmallSet(IterT Begin, IterT End) {
insert(Begin, End);
}
template <typename RangeT>
explicit SmallSet(const iterator_range<RangeT> &R) {
insert(R.begin(), R.end());
}
SmallSet(std::initializer_list<T> L) { insert(L.begin(), L.end()); }
SmallSet &operator=(const SmallSet &) = default;
SmallSet &operator=(SmallSet &&) = default;
[[nodiscard]] bool empty() const { return Vector.empty() && Set.empty(); }
@@ -163,39 +172,16 @@ public:
}
/// count - Return 1 if the element is in the set, 0 otherwise.
size_type count(const T &V) const {
if (isSmall()) {
// Since the collection is small, just do a linear search.
return vfind(V) == Vector.end() ? 0 : 1;
} else {
return Set.count(V);
}
}
size_type count(const T &V) const { return contains(V) ? 1 : 0; }
/// insert - Insert an element into the set if it isn't already there.
/// Returns a pair. The first value of it is an iterator to the inserted
/// element or the existing element in the set. The second value is true
/// if the element is inserted (it was not in the set before).
std::pair<const_iterator, bool> insert(const T &V) {
if (!isSmall()) {
auto [I, Inserted] = Set.insert(V);
return std::make_pair(const_iterator(I), Inserted);
}
std::pair<const_iterator, bool> insert(const T &V) { return insertImpl(V); }
VIterator I = vfind(V);
if (I != Vector.end()) // Don't reinsert if it already exists.
return std::make_pair(const_iterator(I), false);
if (Vector.size() < N) {
Vector.push_back(V);
return std::make_pair(const_iterator(std::prev(Vector.end())), true);
}
// Otherwise, grow from vector to set.
while (!Vector.empty()) {
Set.insert(Vector.back());
Vector.pop_back();
}
return std::make_pair(const_iterator(Set.insert(V).first), true);
std::pair<const_iterator, bool> insert(T &&V) {
return insertImpl(std::move(V));
}
template <typename IterT>
@@ -207,11 +193,11 @@ public:
bool erase(const T &V) {
if (!isSmall())
return Set.erase(V);
for (mutable_iterator I = Vector.begin(), E = Vector.end(); I != E; ++I)
if (*I == V) {
Vector.erase(I);
return true;
}
auto I = vfind(V);
if (I != Vector.end()) {
Vector.erase(I);
return true;
}
return false;
}
@@ -242,8 +228,33 @@ public:
private:
bool isSmall() const { return Set.empty(); }
VIterator vfind(const T &V) const {
for (VIterator I = Vector.begin(), E = Vector.end(); I != E; ++I)
template <typename ArgType>
std::pair<const_iterator, bool> insertImpl(ArgType &&V) {
static_assert(std::is_convertible_v<ArgType, T>,
"ArgType must be convertible to T!");
if (!isSmall()) {
auto [I, Inserted] = Set.insert(std::forward<ArgType>(V));
return {const_iterator(I), Inserted};
}
auto I = vfind(V);
if (I != Vector.end()) // Don't reinsert if it already exists.
return {const_iterator(I), false};
if (Vector.size() < N) {
Vector.push_back(std::forward<ArgType>(V));
return {const_iterator(std::prev(Vector.end())), true};
}
// Otherwise, grow from vector to set.
Set.insert(std::make_move_iterator(Vector.begin()),
std::make_move_iterator(Vector.end()));
Vector.clear();
return {const_iterator(Set.insert(std::forward<ArgType>(V)).first), true};
}
// Handwritten linear search. The use of std::find might hurt performance as
// its implementation may be optimized for larger containers.
typename SmallVector<T, N>::const_iterator vfind(const T &V) const {
for (auto I = Vector.begin(), E = Vector.end(); I != E; ++I)
if (*I == V)
return I;
return Vector.end();

View File

@@ -23,7 +23,6 @@
#endif
#include "wpi/Compiler.h"
#include "wpi/type_traits.h"
#include <algorithm>
#include <cassert>
#include <cstddef>
@@ -82,19 +81,6 @@ protected:
/// This function will report a fatal error if it cannot increase capacity.
void grow_pod(void *FirstEl, size_t MinSize, size_t TSize);
/// If vector was first created with capacity 0, getFirstEl() points to the
/// memory right after, an area unallocated. If a subsequent allocation,
/// that grows the vector, happens to return the same pointer as getFirstEl(),
/// get a new allocation, otherwise isSmall() will falsely return that no
/// allocation was done (true) and the memory will not be freed in the
/// destructor. If a VSize is given (vector size), also copy that many
/// elements to the new allocation - used if realloca fails to increase
/// space, and happens to allocate precisely at BeginX.
/// This is unlikely to be called often, but resolves a memory leak when the
/// situation does occur.
void *replaceAllocation(void *NewElts, size_t TSize, size_t NewCapacity,
size_t VSize = 0);
public:
size_t size() const { return Size; }
size_t capacity() const { return Capacity; }

View File

@@ -15,13 +15,11 @@
#define WPIUTIL_WPI_VERSIONTUPLE_H
#include "wpi/DenseMapInfo.h"
#include "wpi/Hashing.h"
#include <optional>
#include <string>
#include <tuple>
namespace wpi {
template <typename HasherT, wpi::endianness Endianness> class HashBuilder;
class raw_ostream;
/// Represents a version number in the form major[.minor[.subminor[.build]]].

View File

@@ -43,7 +43,8 @@ class iterator_range {
IteratorT begin_iterator, end_iterator;
public:
#if __GNUC__ == 7 || (__GNUC__ == 8 && __GNUC_MINOR__ < 4)
#if defined(__GNUC__) && \
(__GNUC__ == 7 || (__GNUC__ == 8 && __GNUC_MINOR__ < 4))
// Be careful no to break gcc-7 and gcc-8 < 8.4 on the mlir target.
// See https://github.com/llvm/llvm-project/issues/63843
template <typename Container>

View File

@@ -147,7 +147,7 @@ public:
/// So that the stream could keep at least tell() + ExtraSize bytes
/// without re-allocations. reserveExtraSpace() does not change
/// the size/data of the stream.
virtual void reserveExtraSpace(uint64_t ExtraSize) {}
virtual void reserveExtraSpace(uint64_t ExtraSize) { (void)ExtraSize; }
/// Set the stream to be buffered, with an automatically determined buffer
/// size.

View File

@@ -1,81 +0,0 @@
/*
xxHash - Extremely Fast Hash algorithm
Header File
Copyright (C) 2012-2016, Yann Collet.
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
You can contact the author at :
- xxHash source repository : https://github.com/Cyan4973/xxHash
*/
/* based on revision d2df04efcbef7d7f6886d345861e5dfda4edacc1 Removed
* everything but a simple interface for computing XXh64. */
#ifndef WPIUTIL_WPI_XXHASH_H
#define WPIUTIL_WPI_XXHASH_H
#include <stdint.h>
#include <span>
#include <string_view>
namespace wpi {
uint64_t xxHash64(std::string_view Data);
uint64_t xxHash64(std::span<const uint8_t> Data);
uint64_t xxh3_64bits(std::span<const uint8_t> data);
inline uint64_t xxh3_64bits(std::string_view data) {
return xxh3_64bits(std::span(reinterpret_cast<const uint8_t*>(data.data()), data.size()));
}
/*-**********************************************************************
* XXH3 128-bit variant
************************************************************************/
/*!
* @brief The return value from 128-bit hashes.
*
* Stored in little endian order, although the fields themselves are in native
* endianness.
*/
struct XXH128_hash_t {
uint64_t low64; /*!< `value & 0xFFFFFFFFFFFFFFFF` */
uint64_t high64; /*!< `value >> 64` */
/// Convenience equality check operator.
bool operator==(const XXH128_hash_t rhs) const {
return low64 == rhs.low64 && high64 == rhs.high64;
}
};
/// XXH3's 128-bit variant.
XXH128_hash_t xxh3_128bits(std::span<const uint8_t> data);
} // namespace wpi
#endif