Skip to content

Commit f853057

Browse files
fixup! Use TrailingObjects implementation
1 parent 702636e commit f853057

File tree

2 files changed

+64
-43
lines changed

2 files changed

+64
-43
lines changed

llvm/include/llvm/ADT/TrieRawHashMap.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ class ThreadSafeTrieRawHashMapBase {
171171
const unsigned short ContentOffset;
172172
unsigned short NumRootBits;
173173
unsigned short NumSubtrieBits;
174-
struct ImplType;
174+
class ImplType;
175175
// ImplPtr is owned by ThreadSafeTrieRawHashMapBase and needs to be freed in
176176
// destoryImpl.
177177
std::atomic<ImplType *> ImplPtr;

llvm/lib/Support/TrieRawHashMap.cpp

Lines changed: 63 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "llvm/Support/Allocator.h"
1414
#include "llvm/Support/Debug.h"
1515
#include "llvm/Support/ThreadSafeAllocator.h"
16+
#include "llvm/Support/TrailingObjects.h"
1617
#include "llvm/Support/raw_ostream.h"
1718
#include <memory>
1819

@@ -54,9 +55,16 @@ static_assert(sizeof(TrieContent) ==
5455
ThreadSafeTrieRawHashMapBase::TrieContentBaseSize,
5556
"Check header assumption!");
5657

57-
class TrieSubtrie final : public TrieNode {
58+
class TrieSubtrie final
59+
: public TrieNode,
60+
private TrailingObjects<TrieSubtrie, LazyAtomicPointer<TrieNode>> {
5861
public:
59-
TrieNode *get(size_t I) const { return Slots[I].load(); }
62+
using Slot = LazyAtomicPointer<TrieNode>;
63+
64+
Slot &get(size_t I) { return getTrailingObjects<Slot>()[I]; }
65+
TrieNode *load(size_t I) { return get(I).load(); }
66+
67+
unsigned size() const { return Size; }
6068

6169
TrieSubtrie *
6270
sink(size_t I, TrieContent &Content, size_t NumSubtrieBits, size_t NewI,
@@ -68,6 +76,12 @@ class TrieSubtrie final : public TrieNode {
6876

6977
static bool classof(const TrieNode *TN) { return TN->IsSubtrie; }
7078

79+
static constexpr size_t sizeToAlloc(unsigned NumBits) {
80+
assert(NumBits < 20 && "Tries should have fewer than ~1M slots");
81+
size_t Count = 1u << NumBits;
82+
return totalSizeToAlloc<LazyAtomicPointer<TrieNode>>(Count);
83+
}
84+
7185
private:
7286
// FIXME: Use a bitset to speed up access:
7387
//
@@ -91,38 +105,28 @@ class TrieSubtrie final : public TrieNode {
91105
// For debugging.
92106
unsigned StartBit = 0;
93107
unsigned NumBits = 0;
108+
unsigned Size = 0;
94109
friend class llvm::ThreadSafeTrieRawHashMapBase;
110+
friend class TrailingObjects;
95111

96112
public:
97113
/// Linked list for ownership of tries. The pointer is owned by TrieSubtrie.
98114
std::atomic<TrieSubtrie *> Next;
99-
100-
/// The (co-allocated) slots of the subtrie.
101-
MutableArrayRef<LazyAtomicPointer<TrieNode>> Slots;
102115
};
103116
} // end namespace
104117

105-
// Compute the trailing object size in the trie node. This is the size of \c
106-
// Slots in TrieNodes that pointing to the children.
107-
static size_t getTrieTailSize(size_t NumBits) {
108-
return sizeof(TrieNode *) * (1u << NumBits);
109-
}
110-
111118
std::unique_ptr<TrieSubtrie> TrieSubtrie::create(size_t StartBit,
112119
size_t NumBits) {
113-
size_t Size = sizeof(TrieSubtrie) + getTrieTailSize(NumBits);
114-
void *Memory = ::malloc(Size);
120+
void *Memory = ::malloc(sizeToAlloc(NumBits));
115121
TrieSubtrie *S = ::new (Memory) TrieSubtrie(StartBit, NumBits);
116122
return std::unique_ptr<TrieSubtrie>(S);
117123
}
118124

119125
TrieSubtrie::TrieSubtrie(size_t StartBit, size_t NumBits)
120-
: TrieNode(true), StartBit(StartBit), NumBits(NumBits), Next(nullptr),
121-
Slots(reinterpret_cast<LazyAtomicPointer<TrieNode> *>(
122-
reinterpret_cast<char *>(this) + sizeof(TrieSubtrie)),
123-
(1u << NumBits)) {
124-
for (auto *I = Slots.begin(), *E = Slots.end(); I != E; ++I)
125-
new (I) LazyAtomicPointer<TrieNode>(nullptr);
126+
: TrieNode(true), StartBit(StartBit), NumBits(NumBits), Size(1u << NumBits),
127+
Next(nullptr) {
128+
for (unsigned I = 0; I < Size; ++I)
129+
new (&get(I)) Slot(nullptr);
126130

127131
static_assert(
128132
std::is_trivially_destructible<LazyAtomicPointer<TrieNode>>::value,
@@ -140,24 +144,27 @@ TrieSubtrie *TrieSubtrie::sink(
140144
assert(NumSubtrieBits > 0);
141145
std::unique_ptr<TrieSubtrie> S = create(StartBit + NumBits, NumSubtrieBits);
142146

143-
assert(NewI < S->Slots.size());
144-
S->Slots[NewI].store(&Content);
147+
assert(NewI < Size);
148+
S->get(NewI).store(&Content);
145149

146150
// Using compare_exchange to atomically add back the new sub-trie to the trie
147151
// in the place of the exsiting object.
148152
TrieNode *ExistingNode = &Content;
149-
assert(I < Slots.size());
150-
if (Slots[I].compare_exchange_strong(ExistingNode, S.get()))
153+
assert(I < Size);
154+
if (get(I).compare_exchange_strong(ExistingNode, S.get()))
151155
return Saver(std::move(S));
152156

153157
// Another thread created a subtrie already. Return it and let "S" be
154158
// destructed.
155159
return cast<TrieSubtrie>(ExistingNode);
156160
}
157161

158-
struct ThreadSafeTrieRawHashMapBase::ImplType {
162+
class ThreadSafeTrieRawHashMapBase::ImplType final
163+
: private TrailingObjects<ThreadSafeTrieRawHashMapBase::ImplType,
164+
TrieSubtrie> {
165+
public:
159166
static std::unique_ptr<ImplType> create(size_t StartBit, size_t NumBits) {
160-
size_t Size = sizeof(ImplType) + getTrieTailSize(NumBits);
167+
size_t Size = sizeof(ImplType) + TrieSubtrie::sizeToAlloc(NumBits);
161168
void *Memory = ::malloc(Size);
162169
ImplType *Impl = ::new (Memory) ImplType(StartBit, NumBits);
163170
return std::unique_ptr<ImplType>(Impl);
@@ -174,24 +181,30 @@ struct ThreadSafeTrieRawHashMapBase::ImplType {
174181
// Root.Next. This works by repeatedly setting S->Next to a candidate value
175182
// of Root.Next (initially nullptr), then setting Root.Next to S once the
176183
// candidate matches reality.
177-
while (!Root.Next.compare_exchange_weak(CurrentHead, S.get()))
184+
while (!getRoot()->Next.compare_exchange_weak(CurrentHead, S.get()))
178185
S->Next.exchange(CurrentHead);
179186

180187
// Ownership transferred to subtrie successfully. Release the unique_ptr.
181188
return S.release();
182189
}
183190

191+
// Get the root which is the trailing object.
192+
TrieSubtrie *getRoot() { return getTrailingObjects<TrieSubtrie>(); }
193+
184194
static void *operator new(size_t Size) { return ::malloc(Size); }
185195
void operator delete(void *Ptr) { ::free(Ptr); }
186196

187197
/// FIXME: This should take a function that allocates and constructs the
188198
/// content lazily (taking the hash as a separate parameter), in case of
189199
/// collision.
190200
ThreadSafeAllocator<BumpPtrAllocator> ContentAlloc;
191-
TrieSubtrie Root; // Must be last! Tail-allocated.
192201

193202
private:
194-
ImplType(size_t StartBit, size_t NumBits) : Root(StartBit, NumBits) {}
203+
friend class TrailingObjects;
204+
205+
ImplType(size_t StartBit, size_t NumBits) {
206+
::new (getRoot()) TrieSubtrie(StartBit, NumBits);
207+
}
195208
};
196209

197210
ThreadSafeTrieRawHashMapBase::ImplType &
@@ -221,7 +234,7 @@ ThreadSafeTrieRawHashMapBase::find(ArrayRef<uint8_t> Hash) const {
221234
if (!Impl)
222235
return PointerBase();
223236

224-
TrieSubtrie *S = &Impl->Root;
237+
TrieSubtrie *S = Impl->getRoot();
225238
IndexGenerator IndexGen{NumRootBits, NumSubtrieBits, Hash};
226239
size_t Index = IndexGen.next();
227240
while (Index != IndexGen.end()) {
@@ -249,7 +262,7 @@ ThreadSafeTrieRawHashMapBase::PointerBase ThreadSafeTrieRawHashMapBase::insert(
249262
assert(!Hash.empty() && "Uninitialized hash");
250263

251264
ImplType &Impl = getOrCreateImpl();
252-
TrieSubtrie *S = &Impl.Root;
265+
TrieSubtrie *S = Impl.getRoot();
253266
IndexGenerator IndexGen{NumRootBits, NumSubtrieBits, Hash};
254267
size_t Index;
255268
if (Hint.isHint()) {
@@ -263,7 +276,7 @@ ThreadSafeTrieRawHashMapBase::PointerBase ThreadSafeTrieRawHashMapBase::insert(
263276
// Load the node from the slot, allocating and calling the constructor if
264277
// the slot is empty.
265278
bool Generated = false;
266-
TrieNode &Existing = S->Slots[Index].loadOrGenerate([&]() {
279+
TrieNode &Existing = S->get(Index).loadOrGenerate([&]() {
267280
Generated = true;
268281

269282
// Construct the value itself at the tail.
@@ -321,7 +334,15 @@ ThreadSafeTrieRawHashMapBase::ThreadSafeTrieRawHashMapBase(
321334
ContentOffset(ContentOffset),
322335
NumRootBits(NumRootBits ? *NumRootBits : DefaultNumRootBits),
323336
NumSubtrieBits(NumSubtrieBits ? *NumSubtrieBits : DefaultNumSubtrieBits),
324-
ImplPtr(nullptr) {}
337+
ImplPtr(nullptr) {
338+
// Assertion checks for reasonable configuration. The settings below are not
339+
// hard limits on most platforms, but a reasonable configuration should fall
340+
// within those limits.
341+
assert((!NumRootBits || *NumRootBits < 20) &&
342+
"Root should have fewer than ~1M slots");
343+
assert((!NumSubtrieBits || *NumSubtrieBits < 10) &&
344+
"Subtries should have fewer than ~1K slots");
345+
}
325346

326347
ThreadSafeTrieRawHashMapBase::ThreadSafeTrieRawHashMapBase(
327348
ThreadSafeTrieRawHashMapBase &&RHS)
@@ -349,14 +370,14 @@ void ThreadSafeTrieRawHashMapBase::destroyImpl(
349370
// FIXME: Once we have bitsets (see FIXME in TrieSubtrie class), use them
350371
// facilitate sparse iteration here.
351372
if (Destructor)
352-
for (TrieSubtrie *Trie = &Impl->Root; Trie; Trie = Trie->Next.load())
353-
for (auto &Slot : Trie->Slots)
354-
if (auto *Content = dyn_cast_or_null<TrieContent>(Slot.load()))
373+
for (TrieSubtrie *Trie = Impl->getRoot(); Trie; Trie = Trie->Next.load())
374+
for (unsigned I = 0; I < Trie->size(); ++I)
375+
if (auto *Content = dyn_cast_or_null<TrieContent>(Trie->load(I)))
355376
Destructor(Content->getValuePointer());
356377

357378
// Destroy the subtries. Incidentally, this destroys them in the reverse order
358379
// of saving.
359-
TrieSubtrie *Trie = Impl->Root.Next;
380+
TrieSubtrie *Trie = Impl->getRoot()->Next;
360381
while (Trie) {
361382
TrieSubtrie *Next = Trie->Next.exchange(nullptr);
362383
delete Trie;
@@ -369,7 +390,7 @@ ThreadSafeTrieRawHashMapBase::getRoot() const {
369390
ImplType *Impl = ImplPtr.load();
370391
if (!Impl)
371392
return PointerBase();
372-
return PointerBase(&Impl->Root);
393+
return PointerBase(Impl->getRoot());
373394
}
374395

375396
unsigned ThreadSafeTrieRawHashMapBase::getStartBit(
@@ -401,8 +422,8 @@ unsigned ThreadSafeTrieRawHashMapBase::getNumSlotUsed(
401422
if (!S)
402423
return 0;
403424
unsigned Num = 0;
404-
for (unsigned I = 0, E = S->Slots.size(); I < E; ++I)
405-
if (auto *E = S->Slots[I].load())
425+
for (unsigned I = 0, E = S->size(); I < E; ++I)
426+
if (auto *E = S->load(I))
406427
++Num;
407428
return Num;
408429
}
@@ -424,8 +445,8 @@ std::string ThreadSafeTrieRawHashMapBase::getTriePrefixAsString(
424445
while (Current) {
425446
TrieSubtrie *Next = nullptr;
426447
// Find first used slot in the trie.
427-
for (unsigned I = 0, E = Current->Slots.size(); I < E; ++I) {
428-
auto *S = Current->get(I);
448+
for (unsigned I = 0, E = Current->size(); I < E; ++I) {
449+
auto *S = Current->load(I);
429450
if (!S)
430451
continue;
431452

@@ -473,7 +494,7 @@ unsigned ThreadSafeTrieRawHashMapBase::getNumTries() const {
473494
if (!Impl)
474495
return 0;
475496
unsigned Num = 0;
476-
for (TrieSubtrie *Trie = &Impl->Root; Trie; Trie = Trie->Next.load())
497+
for (TrieSubtrie *Trie = Impl->getRoot(); Trie; Trie = Trie->Next.load())
477498
++Num;
478499
return Num;
479500
}

0 commit comments

Comments
 (0)