Skip to content

Commit 9a4a4c3

Browse files
committed
Reapply "[ADT][StringMap] Add ability to precompute and reuse the string hash"
Reverted due to an internally discovered lld crash, which turned out to be an existing lld bug that got tickled by this changes. That's addressed in dee8786 so let's have another go with this change. Original commit message: 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 This reverts commit f976719. Effectively reapplying 67c631d.
1 parent 1ac6846 commit 9a4a4c3

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)