13
13
#include " llvm/Support/Allocator.h"
14
14
#include " llvm/Support/Debug.h"
15
15
#include " llvm/Support/ThreadSafeAllocator.h"
16
+ #include " llvm/Support/TrailingObjects.h"
16
17
#include " llvm/Support/raw_ostream.h"
17
18
#include < memory>
18
19
@@ -54,9 +55,16 @@ static_assert(sizeof(TrieContent) ==
54
55
ThreadSafeTrieRawHashMapBase::TrieContentBaseSize,
55
56
"Check header assumption!");
56
57
57
- class TrieSubtrie final : public TrieNode {
58
+ class TrieSubtrie final
59
+ : public TrieNode,
60
+ private TrailingObjects<TrieSubtrie, LazyAtomicPointer<TrieNode>> {
58
61
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; }
60
68
61
69
TrieSubtrie *
62
70
sink (size_t I, TrieContent &Content, size_t NumSubtrieBits, size_t NewI,
@@ -68,6 +76,12 @@ class TrieSubtrie final : public TrieNode {
68
76
69
77
static bool classof (const TrieNode *TN) { return TN->IsSubtrie ; }
70
78
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
+
71
85
private:
72
86
// FIXME: Use a bitset to speed up access:
73
87
//
@@ -91,38 +105,28 @@ class TrieSubtrie final : public TrieNode {
91
105
// For debugging.
92
106
unsigned StartBit = 0 ;
93
107
unsigned NumBits = 0 ;
108
+ unsigned Size = 0 ;
94
109
friend class llvm ::ThreadSafeTrieRawHashMapBase;
110
+ friend class TrailingObjects ;
95
111
96
112
public:
97
113
// / Linked list for ownership of tries. The pointer is owned by TrieSubtrie.
98
114
std::atomic<TrieSubtrie *> Next;
99
-
100
- // / The (co-allocated) slots of the subtrie.
101
- MutableArrayRef<LazyAtomicPointer<TrieNode>> Slots;
102
115
};
103
116
} // end namespace
104
117
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
-
111
118
std::unique_ptr<TrieSubtrie> TrieSubtrie::create (size_t StartBit,
112
119
size_t NumBits) {
113
- size_t Size = sizeof (TrieSubtrie) + getTrieTailSize (NumBits);
114
- void *Memory = ::malloc (Size);
120
+ void *Memory = ::malloc (sizeToAlloc (NumBits));
115
121
TrieSubtrie *S = ::new (Memory) TrieSubtrie (StartBit, NumBits);
116
122
return std::unique_ptr<TrieSubtrie>(S);
117
123
}
118
124
119
125
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 );
126
130
127
131
static_assert (
128
132
std::is_trivially_destructible<LazyAtomicPointer<TrieNode>>::value,
@@ -140,24 +144,27 @@ TrieSubtrie *TrieSubtrie::sink(
140
144
assert (NumSubtrieBits > 0 );
141
145
std::unique_ptr<TrieSubtrie> S = create (StartBit + NumBits, NumSubtrieBits);
142
146
143
- assert (NewI < S-> Slots . size () );
144
- S->Slots [ NewI] .store (&Content);
147
+ assert (NewI < Size );
148
+ S->get ( NewI) .store (&Content);
145
149
146
150
// Using compare_exchange to atomically add back the new sub-trie to the trie
147
151
// in the place of the exsiting object.
148
152
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 ()))
151
155
return Saver (std::move (S));
152
156
153
157
// Another thread created a subtrie already. Return it and let "S" be
154
158
// destructed.
155
159
return cast<TrieSubtrie>(ExistingNode);
156
160
}
157
161
158
- struct ThreadSafeTrieRawHashMapBase ::ImplType {
162
+ class ThreadSafeTrieRawHashMapBase ::ImplType final
163
+ : private TrailingObjects<ThreadSafeTrieRawHashMapBase::ImplType,
164
+ TrieSubtrie> {
165
+ public:
159
166
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);
161
168
void *Memory = ::malloc (Size);
162
169
ImplType *Impl = ::new (Memory) ImplType (StartBit, NumBits);
163
170
return std::unique_ptr<ImplType>(Impl);
@@ -174,24 +181,30 @@ struct ThreadSafeTrieRawHashMapBase::ImplType {
174
181
// Root.Next. This works by repeatedly setting S->Next to a candidate value
175
182
// of Root.Next (initially nullptr), then setting Root.Next to S once the
176
183
// candidate matches reality.
177
- while (!Root. Next .compare_exchange_weak (CurrentHead, S.get ()))
184
+ while (!getRoot ()-> Next .compare_exchange_weak (CurrentHead, S.get ()))
178
185
S->Next .exchange (CurrentHead);
179
186
180
187
// Ownership transferred to subtrie successfully. Release the unique_ptr.
181
188
return S.release ();
182
189
}
183
190
191
+ // Get the root which is the trailing object.
192
+ TrieSubtrie *getRoot () { return getTrailingObjects<TrieSubtrie>(); }
193
+
184
194
static void *operator new (size_t Size) { return ::malloc (Size); }
185
195
void operator delete (void *Ptr) { ::free (Ptr); }
186
196
187
197
// / FIXME: This should take a function that allocates and constructs the
188
198
// / content lazily (taking the hash as a separate parameter), in case of
189
199
// / collision.
190
200
ThreadSafeAllocator<BumpPtrAllocator> ContentAlloc;
191
- TrieSubtrie Root; // Must be last! Tail-allocated.
192
201
193
202
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
+ }
195
208
};
196
209
197
210
ThreadSafeTrieRawHashMapBase::ImplType &
@@ -221,7 +234,7 @@ ThreadSafeTrieRawHashMapBase::find(ArrayRef<uint8_t> Hash) const {
221
234
if (!Impl)
222
235
return PointerBase ();
223
236
224
- TrieSubtrie *S = & Impl->Root ;
237
+ TrieSubtrie *S = Impl->getRoot () ;
225
238
IndexGenerator IndexGen{NumRootBits, NumSubtrieBits, Hash};
226
239
size_t Index = IndexGen.next ();
227
240
while (Index != IndexGen.end ()) {
@@ -249,7 +262,7 @@ ThreadSafeTrieRawHashMapBase::PointerBase ThreadSafeTrieRawHashMapBase::insert(
249
262
assert (!Hash.empty () && " Uninitialized hash" );
250
263
251
264
ImplType &Impl = getOrCreateImpl ();
252
- TrieSubtrie *S = & Impl.Root ;
265
+ TrieSubtrie *S = Impl.getRoot () ;
253
266
IndexGenerator IndexGen{NumRootBits, NumSubtrieBits, Hash};
254
267
size_t Index;
255
268
if (Hint.isHint ()) {
@@ -263,7 +276,7 @@ ThreadSafeTrieRawHashMapBase::PointerBase ThreadSafeTrieRawHashMapBase::insert(
263
276
// Load the node from the slot, allocating and calling the constructor if
264
277
// the slot is empty.
265
278
bool Generated = false ;
266
- TrieNode &Existing = S->Slots [ Index] .loadOrGenerate ([&]() {
279
+ TrieNode &Existing = S->get ( Index) .loadOrGenerate ([&]() {
267
280
Generated = true ;
268
281
269
282
// Construct the value itself at the tail.
@@ -321,7 +334,15 @@ ThreadSafeTrieRawHashMapBase::ThreadSafeTrieRawHashMapBase(
321
334
ContentOffset(ContentOffset),
322
335
NumRootBits(NumRootBits ? *NumRootBits : DefaultNumRootBits),
323
336
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
+ }
325
346
326
347
ThreadSafeTrieRawHashMapBase::ThreadSafeTrieRawHashMapBase (
327
348
ThreadSafeTrieRawHashMapBase &&RHS)
@@ -349,14 +370,14 @@ void ThreadSafeTrieRawHashMapBase::destroyImpl(
349
370
// FIXME: Once we have bitsets (see FIXME in TrieSubtrie class), use them
350
371
// facilitate sparse iteration here.
351
372
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 )))
355
376
Destructor (Content->getValuePointer ());
356
377
357
378
// Destroy the subtries. Incidentally, this destroys them in the reverse order
358
379
// of saving.
359
- TrieSubtrie *Trie = Impl->Root . Next ;
380
+ TrieSubtrie *Trie = Impl->getRoot ()-> Next ;
360
381
while (Trie) {
361
382
TrieSubtrie *Next = Trie->Next .exchange (nullptr );
362
383
delete Trie;
@@ -369,7 +390,7 @@ ThreadSafeTrieRawHashMapBase::getRoot() const {
369
390
ImplType *Impl = ImplPtr.load ();
370
391
if (!Impl)
371
392
return PointerBase ();
372
- return PointerBase (& Impl->Root );
393
+ return PointerBase (Impl->getRoot () );
373
394
}
374
395
375
396
unsigned ThreadSafeTrieRawHashMapBase::getStartBit (
@@ -401,8 +422,8 @@ unsigned ThreadSafeTrieRawHashMapBase::getNumSlotUsed(
401
422
if (!S)
402
423
return 0 ;
403
424
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 ))
406
427
++Num;
407
428
return Num;
408
429
}
@@ -424,8 +445,8 @@ std::string ThreadSafeTrieRawHashMapBase::getTriePrefixAsString(
424
445
while (Current) {
425
446
TrieSubtrie *Next = nullptr ;
426
447
// 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);
429
450
if (!S)
430
451
continue ;
431
452
@@ -473,7 +494,7 @@ unsigned ThreadSafeTrieRawHashMapBase::getNumTries() const {
473
494
if (!Impl)
474
495
return 0 ;
475
496
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 ())
477
498
++Num;
478
499
return Num;
479
500
}
0 commit comments