Skip to content

Commit 67c631d

Browse files
committed
[ADT][StringMap] Add ability to precompute and reuse the string hash
Useful for lldb's const string pool, using the hash to determine which string map to lock and query/insert. Derived from https://reviews.llvm.org/D122974 by Luboš Luňák
1 parent 95d6aa2 commit 67c631d

File tree

3 files changed

+63
-12
lines changed

3 files changed

+63
-12
lines changed

llvm/include/llvm/ADT/StringMap.h

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "llvm/ADT/StringMapEntry.h"
1818
#include "llvm/ADT/iterator.h"
1919
#include "llvm/Support/AllocatorBase.h"
20+
#include "llvm/Support/DJB.h"
2021
#include "llvm/Support/PointerLikeTypeTraits.h"
2122
#include <initializer_list>
2223
#include <iterator>
@@ -60,12 +61,20 @@ class StringMapImpl {
6061
/// specified bucket will be non-null. Otherwise, it will be null. In either
6162
/// case, the FullHashValue field of the bucket will be set to the hash value
6263
/// of the string.
63-
unsigned LookupBucketFor(StringRef Key);
64+
unsigned LookupBucketFor(StringRef Key) {
65+
return LookupBucketFor(Key, hash(Key));
66+
}
67+
68+
/// Overload that explicitly takes precomputed hash(Key).
69+
unsigned LookupBucketFor(StringRef Key, uint32_t FullHashValue);
6470

6571
/// FindKey - Look up the bucket that contains the specified key. If it exists
6672
/// in the map, return the bucket number of the key. Otherwise return -1.
6773
/// This does not modify the map.
68-
int FindKey(StringRef Key) const;
74+
int FindKey(StringRef Key) const { return FindKey(Key, hash(Key)); }
75+
76+
/// Overload that explicitly takes precomputed hash(Key).
77+
int FindKey(StringRef Key, uint32_t FullHashValue) const;
6978

7079
/// RemoveKey - Remove the specified StringMapEntry from the table, but do not
7180
/// delete it. This aborts if the value isn't in the table.
@@ -94,6 +103,13 @@ class StringMapImpl {
94103
bool empty() const { return NumItems == 0; }
95104
unsigned size() const { return NumItems; }
96105

106+
/// Returns the hash value that will be used for the given string.
107+
/// This allows precomputing the value and passing it explicitly
108+
/// to some of the functions.
109+
/// The implementation of this function is not guaranteed to be stable
110+
/// and may change.
111+
static uint32_t hash(StringRef Key);
112+
97113
void swap(StringMapImpl &Other) {
98114
std::swap(TheTable, Other.TheTable);
99115
std::swap(NumBuckets, Other.NumBuckets);
@@ -215,15 +231,19 @@ class LLVM_ALLOCATORHOLDER_EMPTYBASE StringMap
215231
StringMapKeyIterator<ValueTy>(end()));
216232
}
217233

218-
iterator find(StringRef Key) {
219-
int Bucket = FindKey(Key);
234+
iterator find(StringRef Key) { return find(Key, hash(Key)); }
235+
236+
iterator find(StringRef Key, uint32_t FullHashValue) {
237+
int Bucket = FindKey(Key, FullHashValue);
220238
if (Bucket == -1)
221239
return end();
222240
return iterator(TheTable + Bucket, true);
223241
}
224242

225-
const_iterator find(StringRef Key) const {
226-
int Bucket = FindKey(Key);
243+
const_iterator find(StringRef Key) const { return find(Key, hash(Key)); }
244+
245+
const_iterator find(StringRef Key, uint32_t FullHashValue) const {
246+
int Bucket = FindKey(Key, FullHashValue);
227247
if (Bucket == -1)
228248
return end();
229249
return const_iterator(TheTable + Bucket, true);
@@ -305,7 +325,13 @@ class LLVM_ALLOCATORHOLDER_EMPTYBASE StringMap
305325
/// if and only if the insertion takes place, and the iterator component of
306326
/// the pair points to the element with key equivalent to the key of the pair.
307327
std::pair<iterator, bool> insert(std::pair<StringRef, ValueTy> KV) {
308-
return try_emplace(KV.first, std::move(KV.second));
328+
return try_emplace_with_hash(KV.first, hash(KV.first),
329+
std::move(KV.second));
330+
}
331+
332+
std::pair<iterator, bool> insert(std::pair<StringRef, ValueTy> KV,
333+
uint32_t FullHashValue) {
334+
return try_emplace_with_hash(KV.first, FullHashValue, std::move(KV.second));
309335
}
310336

311337
/// Inserts elements from range [first, last). If multiple elements in the
@@ -339,7 +365,14 @@ class LLVM_ALLOCATORHOLDER_EMPTYBASE StringMap
339365
/// the pair points to the element with key equivalent to the key of the pair.
340366
template <typename... ArgsTy>
341367
std::pair<iterator, bool> try_emplace(StringRef Key, ArgsTy &&...Args) {
342-
unsigned BucketNo = LookupBucketFor(Key);
368+
return try_emplace_with_hash(Key, hash(Key), std::forward<ArgsTy>(Args)...);
369+
}
370+
371+
template <typename... ArgsTy>
372+
std::pair<iterator, bool> try_emplace_with_hash(StringRef Key,
373+
uint32_t FullHashValue,
374+
ArgsTy &&...Args) {
375+
unsigned BucketNo = LookupBucketFor(Key, FullHashValue);
343376
StringMapEntryBase *&Bucket = TheTable[BucketNo];
344377
if (Bucket && Bucket != getTombstoneVal())
345378
return std::make_pair(iterator(TheTable + BucketNo, false),

llvm/lib/Support/StringMap.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ static inline unsigned *getHashTable(StringMapEntryBase **TheTable,
4343
return reinterpret_cast<unsigned *>(TheTable + NumBuckets + 1);
4444
}
4545

46+
uint32_t StringMapImpl::hash(StringRef Key) { return xxh3_64bits(Key); }
47+
4648
StringMapImpl::StringMapImpl(unsigned InitSize, unsigned itemSize) {
4749
ItemSize = itemSize;
4850

@@ -81,11 +83,14 @@ void StringMapImpl::init(unsigned InitSize) {
8183
/// specified bucket will be non-null. Otherwise, it will be null. In either
8284
/// case, the FullHashValue field of the bucket will be set to the hash value
8385
/// of the string.
84-
unsigned StringMapImpl::LookupBucketFor(StringRef Name) {
86+
unsigned StringMapImpl::LookupBucketFor(StringRef Name,
87+
uint32_t FullHashValue) {
88+
#ifdef EXPENSIVE_CHECKS
89+
assert(FullHashValue == hash(Name));
90+
#endif
8591
// Hash table unallocated so far?
8692
if (NumBuckets == 0)
8793
init(16);
88-
unsigned FullHashValue = xxh3_64bits(Name);
8994
if (shouldReverseIterate())
9095
FullHashValue = ~FullHashValue;
9196
unsigned BucketNo = FullHashValue & (NumBuckets - 1);
@@ -139,10 +144,12 @@ unsigned StringMapImpl::LookupBucketFor(StringRef Name) {
139144
/// FindKey - Look up the bucket that contains the specified key. If it exists
140145
/// in the map, return the bucket number of the key. Otherwise return -1.
141146
/// This does not modify the map.
142-
int StringMapImpl::FindKey(StringRef Key) const {
147+
int StringMapImpl::FindKey(StringRef Key, uint32_t FullHashValue) const {
143148
if (NumBuckets == 0)
144149
return -1; // Really empty table?
145-
unsigned FullHashValue = xxh3_64bits(Key);
150+
#ifdef EXPENSIVE_CHECKS
151+
assert(FullHashValue == hash(Key);
152+
#endif
146153
if (shouldReverseIterate())
147154
FullHashValue = ~FullHashValue;
148155
unsigned BucketNo = FullHashValue & (NumBuckets - 1);

llvm/unittests/ADT/StringMapTest.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,17 @@ TEST_F(StringMapTest, NotEqualWithDifferentValues) {
487487
ASSERT_TRUE(B != A);
488488
}
489489

490+
TEST_F(StringMapTest, PrecomputedHash) {
491+
StringMap<int> A;
492+
StringRef Key = "foo";
493+
int Value = 42;
494+
uint64_t Hash = StringMap<int>::hash(Key);
495+
A.insert({"foo", Value}, Hash);
496+
auto I = A.find(Key, Hash);
497+
ASSERT_NE(I, A.end());
498+
ASSERT_EQ(I->second, Value);
499+
}
500+
490501
struct Countable {
491502
int &InstanceCount;
492503
int Number;

0 commit comments

Comments
 (0)