v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
free-list.cc
Go to the documentation of this file.
1// Copyright 2020 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
7#include "src/base/macros.h"
10#include "src/heap/heap.h"
14
15namespace v8 {
16namespace internal {
17
18// -----------------------------------------------------------------------------
19// Free lists for old object spaces implementation
20
22 if (is_linked(owner) && !top().is_null()) {
24 }
25 set_prev(nullptr);
26 set_next(nullptr);
27}
28
30 Unlink(owner);
32 available_ = 0;
33}
34
36 size_t* node_size) {
37 Tagged<FreeSpace> node = top();
38 DCHECK(!node.is_null());
39 DCHECK(MemoryChunk::FromHeapObject(node)->CanAllocate());
40 if (static_cast<size_t>(node->Size()) < minimum_size) {
41 *node_size = 0;
42 return FreeSpace();
43 }
44 set_top(node->next());
45 *node_size = node->Size();
47 return node;
48}
49
51 size_t* node_size) {
52 Tagged<FreeSpace> prev_non_evac_node;
53 for (Tagged<FreeSpace> cur_node = top(); !cur_node.is_null();
54 cur_node = cur_node->next()) {
55 DCHECK(MemoryChunk::FromHeapObject(cur_node)->CanAllocate());
56 size_t size = cur_node->size(kRelaxedLoad);
57 if (size >= minimum_size) {
58 DCHECK_GE(available_, size);
60 if (cur_node == top()) {
61 set_top(cur_node->next());
62 }
63 if (!prev_non_evac_node.is_null()) {
64 if (MemoryChunk::FromHeapObject(prev_non_evac_node)->executable()) {
65 WritableJitPage jit_page(prev_non_evac_node->address(),
66 prev_non_evac_node->Size());
67 WritableFreeSpace free_space = jit_page.FreeRange(
68 prev_non_evac_node->address(), prev_non_evac_node->Size());
69 prev_non_evac_node->SetNext(free_space, cur_node->next());
70 } else {
71 prev_non_evac_node->SetNext(
73 prev_non_evac_node->address(), prev_non_evac_node->Size()),
74 cur_node->next());
75 }
76 }
77 *node_size = size;
78 return cur_node;
79 }
80
81 prev_non_evac_node = cur_node;
82 }
83 return FreeSpace();
84}
85
86void FreeListCategory::Free(const WritableFreeSpace& writable_free_space,
87 FreeMode mode, FreeList* owner) {
88 Tagged<FreeSpace> free_space =
89 Cast<FreeSpace>(HeapObject::FromAddress(writable_free_space.Address()));
90 DCHECK_EQ(free_space->Size(), writable_free_space.Size());
91 free_space->SetNext(writable_free_space, top());
92 set_top(free_space);
93 size_t size_in_bytes = writable_free_space.Size();
94 available_ += size_in_bytes;
95 if (mode == kLinkCategory) {
96 if (is_linked(owner)) {
97 owner->IncreaseAvailableBytes(size_in_bytes);
98 } else {
99 owner->AddCategory(this);
100 }
101 }
102}
103
105 Tagged<Map> free_space_map = ReadOnlyRoots(heap).free_space_map();
107 while (!n.is_null()) {
108 ObjectSlot map_slot = n->map_slot();
109 if (map_slot.contains_map_value(kNullAddress)) {
110 map_slot.store_map(free_space_map);
111 } else {
112 DCHECK(map_slot.contains_map_value(free_space_map.ptr()));
113 }
114 n = n->next();
115 }
116}
117
119 DCHECK(!is_linked(owner));
120 owner->AddCategory(this);
121}
122
123// ------------------------------------------------
124// Generic FreeList methods (alloc/free related)
125
126FreeList::FreeList(int number_of_categories, size_t min_block_size)
127 : number_of_categories_(number_of_categories),
128 last_category_(number_of_categories - 1),
129 min_block_size_(min_block_size) {}
130
131std::unique_ptr<FreeList> FreeList::CreateFreeList() {
132 return std::make_unique<FreeListManyCachedOrigin>();
133}
134
135std::unique_ptr<FreeList> FreeList::CreateFreeListForNewSpace() {
136 return std::make_unique<FreeListManyCachedFastPathForNewSpace>();
137}
138
140 size_t minimum_size,
141 size_t* node_size) {
142 FreeListCategory* category = categories_[type];
143 if (category == nullptr) return FreeSpace();
144 Tagged<FreeSpace> node = category->PickNodeFromList(minimum_size, node_size);
145 if (!node.is_null()) {
146 DecreaseAvailableBytes(*node_size);
148 }
149 if (category->is_empty()) {
150 RemoveCategory(category);
151 }
152 return node;
153}
154
156 size_t minimum_size,
157 size_t* node_size) {
158 FreeListCategoryIterator it(this, type);
160 while (it.HasNext()) {
161 FreeListCategory* current = it.Next();
162 node = current->SearchForNodeInList(minimum_size, node_size);
163 if (!node.is_null()) {
164 DecreaseAvailableBytes(*node_size);
166 if (current->is_empty()) {
167 RemoveCategory(current);
168 }
169 return node;
170 }
171 }
172 return node;
173}
174
175size_t FreeList::Free(const WritableFreeSpace& free_space, FreeMode mode) {
176 Address start = free_space.Address();
177 size_t size_in_bytes = free_space.Size();
179 page->DecreaseAllocatedBytes(size_in_bytes);
180
181 // Blocks have to be a minimum size to hold free list items.
182 if (size_in_bytes < min_block_size_) {
183 page->add_wasted_memory(size_in_bytes);
184 return size_in_bytes;
185 }
186
187 // Insert other blocks at the head of a free list of the appropriate
188 // magnitude.
190 page->free_list_category(type)->Free(free_space, mode, this);
191 DCHECK_EQ(page->AvailableInFreeList(),
192 page->AvailableInFreeListFromAllocatedBytes());
193 return 0;
194}
195
196// ------------------------------------------------
197// FreeListMany implementation
198
199constexpr unsigned int FreeListMany::categories_min[kNumberOfCategories];
200
201FreeListMany::FreeListMany() : FreeList(kNumberOfCategories, kMinBlockSize) {
202 // Initializing base (FreeList) fields
204 Reset();
205}
206
208
210 FreeListCategoryType minimum_category =
211 SelectFreeListCategoryType(size_in_bytes);
212 PageMetadata* page = nullptr;
213 for (int cat = minimum_category + 1; !page && cat <= last_category_; cat++) {
214 page = GetPageForCategoryType(cat);
215 }
216 if (!page) {
217 // Might return a page in which |size_in_bytes| will not fit.
218 page = GetPageForCategoryType(minimum_category);
219 }
220 return page;
221}
222
224 size_t* node_size,
225 AllocationOrigin origin) {
226 DCHECK_GE(kMaxBlockSize, size_in_bytes);
229 for (int i = type; i < last_category_ && node.is_null(); i++) {
230 node = TryFindNodeIn(static_cast<FreeListCategoryType>(i), size_in_bytes,
231 node_size);
232 }
233
234 if (node.is_null()) {
235 // Searching each element of the last category.
236 node = SearchForNodeInList(last_category_, size_in_bytes, node_size);
237 }
238
239 if (!node.is_null()) {
241 }
242
244 return node;
245}
246
247// ------------------------------------------------
248// FreeListManyCached implementation
249
251
256
261
263 bool was_added = FreeList::AddCategory(category);
264
265 // Updating cache
266 if (was_added) {
268 }
269
270#ifdef DEBUG
271 CheckCacheIntegrity();
272#endif
273
274 return was_added;
275}
276
278 FreeList::RemoveCategory(category);
279
280 // Updating cache
281 int type = category->type_;
282 if (categories_[type] == nullptr) {
284 }
285
286#ifdef DEBUG
287 CheckCacheIntegrity();
288#endif
289}
290
292 FreeMode mode) {
293 Address start = free_space.Address();
294 size_t size_in_bytes = free_space.Size();
296 page->DecreaseAllocatedBytes(size_in_bytes);
297
298 // Blocks have to be a minimum size to hold free list items.
299 if (size_in_bytes < min_block_size_) {
300 page->add_wasted_memory(size_in_bytes);
301 return size_in_bytes;
302 }
303
304 // Insert other blocks at the head of a free list of the appropriate
305 // magnitude.
307 page->free_list_category(type)->Free(free_space, mode, this);
308
309 // Updating cache
310 if (mode == kLinkCategory) {
312
313#ifdef DEBUG
314 CheckCacheIntegrity();
315#endif
316 }
317
318 DCHECK_EQ(page->AvailableInFreeList(),
319 page->AvailableInFreeListFromAllocatedBytes());
320 return 0;
321}
322
324 size_t* node_size,
325 AllocationOrigin origin) {
326 USE(origin);
327 DCHECK_GE(kMaxBlockSize, size_in_bytes);
328
332 for (; type < last_category_; type = next_nonempty_category[type + 1]) {
333 node = TryFindNodeIn(type, size_in_bytes, node_size);
334 if (!node.is_null()) break;
335 }
336
337 if (node.is_null()) {
338 // Searching each element of the last category.
339 type = last_category_;
340 node = SearchForNodeInList(type, size_in_bytes, node_size);
341 }
342
343 // Updating cache
344 if (!node.is_null() && categories_[type] == nullptr) {
346 }
347
348#ifdef DEBUG
349 CheckCacheIntegrity();
350#endif
351
352 if (!node.is_null()) {
354 }
355
357 return node;
358}
359
360// ------------------------------------------------
361// FreeListManyCachedFastPathBase implementation
362
364 size_t size_in_bytes, size_t* node_size, AllocationOrigin origin) {
365 USE(origin);
366 DCHECK_GE(kMaxBlockSize, size_in_bytes);
368
369 // Fast path part 1: searching the last categories
370 FreeListCategoryType first_category =
372 FreeListCategoryType type = first_category;
373 for (type = next_nonempty_category[type]; type <= last_category_;
374 type = next_nonempty_category[type + 1]) {
375 node = TryFindNodeIn(type, size_in_bytes, node_size);
376 if (!node.is_null()) break;
377 }
378
379 // Fast path part 2: searching the medium categories for tiny objects
381 if (node.is_null()) {
382 if (size_in_bytes <= kTinyObjectMaxSize) {
383 DCHECK_EQ(kFastPathFirstCategory, first_category);
386 type = next_nonempty_category[type + 1]) {
387 node = TryFindNodeIn(type, size_in_bytes, node_size);
388 if (!node.is_null()) break;
389 }
390 first_category = kFastPathFallBackTiny;
391 }
392 }
393 }
394
395 // Searching the last category
396 if (node.is_null()) {
397 // Searching each element of the last category.
398 type = last_category_;
399 node = SearchForNodeInList(type, size_in_bytes, node_size);
400 }
401
402 // Finally, search the most precise category
403 if (node.is_null()) {
404 type = SelectFreeListCategoryType(size_in_bytes);
405 for (type = next_nonempty_category[type]; type < first_category;
406 type = next_nonempty_category[type + 1]) {
407 node = TryFindNodeIn(type, size_in_bytes, node_size);
408 if (!node.is_null()) break;
409 }
410 }
411
412 if (!node.is_null()) {
413 if (categories_[type] == nullptr) UpdateCacheAfterRemoval(type);
415 }
416
417#ifdef DEBUG
418 CheckCacheIntegrity();
419#endif
420
422 return node;
423}
424
425// ------------------------------------------------
426// FreeListManyCachedOrigin implementation
427
429 size_t* node_size,
430 AllocationOrigin origin) {
431 if (origin == AllocationOrigin::kGC) {
432 return FreeListManyCached::Allocate(size_in_bytes, node_size, origin);
433 } else {
434 return FreeListManyCachedFastPath::Allocate(size_in_bytes, node_size,
435 origin);
436 }
437}
438
439// ------------------------------------------------
440// Generic FreeList methods (non alloc/free related)
441
444 [this](FreeListCategory* category) { category->Reset(this); });
445 for (int i = kFirstCategory; i < number_of_categories_; i++) {
446 categories_[i] = nullptr;
447 }
448 wasted_bytes_ = 0;
449 available_ = 0;
450}
451
453 DCHECK(v8_flags.black_allocated_pages);
455 if (!category->is_empty()) {
456 auto* chunk = MemoryChunk::FromHeapObject(category->top());
457 if (chunk->IsFlagSet(MemoryChunk::BLACK_ALLOCATED)) {
458 category->Unlink(this);
459 return;
460 }
461 }
462 category->Reset(this);
463 });
464 for (int i = kFirstCategory; i < number_of_categories_; i++) {
465 categories_[i] = nullptr;
466 }
467 wasted_bytes_ = 0;
468 available_ = 0;
469}
470
472 size_t sum = 0;
473 page->ForAllFreeListCategories([this, &sum](FreeListCategory* category) {
474 sum += category->available();
475 RemoveCategory(category);
476 category->Reset(this);
477 });
478 page->add_wasted_memory(sum);
479}
480
485
487 FreeListCategoryType type = category->type_;
490
491 if (category->is_empty()) return false;
492 DCHECK_NE(top, category);
493
494 // Common double-linked list insertion.
495 if (top != nullptr) {
496 top->set_prev(category);
497 }
498 category->set_next(top);
499 categories_[type] = category;
500
502 return true;
503}
504
506 FreeListCategoryType type = category->type_;
509
510 if (category->is_linked(this)) {
512 }
513
514 // Common double-linked list removal.
515 if (top == category) {
516 categories_[type] = category->next();
517 }
518 if (category->prev() != nullptr) {
519 category->prev()->set_next(category->next());
520 }
521 if (category->next() != nullptr) {
522 category->next()->set_prev(category->prev());
523 }
524 category->set_next(nullptr);
525 category->set_prev(nullptr);
526}
527
529 FreeListCategoryIterator it(this, type);
530 PrintF("FreeList[%p, top=%p, %d] ", static_cast<void*>(this),
531 static_cast<void*>(categories_[type]), type);
532 while (it.HasNext()) {
533 FreeListCategory* current = it.Next();
534 PrintF("%p -> ", static_cast<void*>(current));
535 }
536 PrintF("null\n");
537}
538
540 size_t sum = 0;
541 Tagged<FreeSpace> cur = top();
542 while (!cur.is_null()) {
543 // We can't use "cur->map()" here because both cur's map and the
544 // root can be null during bootstrapping.
545 DCHECK(
546 cur->map_slot().contains_map_value(PageMetadata::FromHeapObject(cur)
547 ->heap()
548 ->isolate()
549 ->root(RootIndex::kFreeSpaceMap)
550 .ptr()));
551 sum += cur->size(kRelaxedLoad);
552 cur = cur->next();
553 }
554 return sum;
555}
557 int length = 0;
558 Tagged<FreeSpace> cur = top();
559 while (!cur.is_null()) {
560 length++;
561 cur = cur->next();
562 }
563 return length;
564}
565
566#ifdef DEBUG
567bool FreeList::IsVeryLong() {
568 int len = 0;
569 for (int i = kFirstCategory; i < number_of_categories_; i++) {
570 FreeListCategoryIterator it(this, static_cast<FreeListCategoryType>(i));
571 while (it.HasNext()) {
572 len += it.Next()->FreeListLength();
573 if (len >= FreeListCategory::kVeryLongFreeList) return true;
574 }
575 }
576 return false;
577}
578
579// This can take a very long time because it is linear in the number of entries
580// on the free list, so it should not be called if FreeListLength returns
581// kVeryLongFreeList.
582size_t FreeList::SumFreeLists() {
583 size_t sum = 0;
585 [&sum](FreeListCategory* category) { sum += category->SumFreeList(); });
586 return sum;
587}
588#endif
589
590} // namespace internal
591} // namespace v8
void Relink(FreeList *owner)
Definition free-list.cc:118
uint32_t available() const
Definition free-list.h:80
Tagged< FreeSpace > top()
Definition free-list.h:102
bool is_linked(FreeList *owner) const
FreeListCategory * next()
Definition free-list.h:106
void RepairFreeList(Heap *heap)
Definition free-list.cc:104
static constexpr int kVeryLongFreeList
Definition free-list.h:96
void Reset(FreeList *owner)
Definition free-list.cc:29
FreeListCategory * prev()
Definition free-list.h:104
FreeListCategoryType type_
Definition free-list.h:110
void Free(const WritableFreeSpace &writable_free_space, FreeMode mode, FreeList *owner)
Definition free-list.cc:86
Tagged< FreeSpace > SearchForNodeInList(size_t minimum_size, size_t *node_size)
Definition free-list.cc:50
void UpdateCountersAfterAllocation(size_t allocation_size)
void set_prev(FreeListCategory *prev)
Definition free-list.h:105
V8_EXPORT_PRIVATE Tagged< FreeSpace > PickNodeFromList(size_t minimum_size, size_t *node_size)
Definition free-list.cc:35
void set_top(Tagged< FreeSpace > top)
Definition free-list.h:103
void set_next(FreeListCategory *next)
Definition free-list.h:107
void Unlink(FreeList *owner)
Definition free-list.cc:21
FreeListCategoryType SelectFastAllocationFreeListCategoryType(size_t size_in_bytes)
Definition free-list.h:466
V8_WARN_UNUSED_RESULT Tagged< FreeSpace > Allocate(size_t size_in_bytes, size_t *node_size, AllocationOrigin origin) override
Definition free-list.cc:363
static const FreeListCategoryType kFastPathFallBackTiny
Definition free-list.h:460
static const FreeListCategoryType kFastPathFirstCategory
Definition free-list.h:455
V8_WARN_UNUSED_RESULT Tagged< FreeSpace > Allocate(size_t size_in_bytes, size_t *node_size, AllocationOrigin origin) override
Definition free-list.cc:428
void ResetForNonBlackAllocatedPages() override
Definition free-list.cc:257
bool AddCategory(FreeListCategory *category) override
Definition free-list.cc:262
size_t Free(const WritableFreeSpace &free_space, FreeMode mode) override
Definition free-list.cc:291
V8_WARN_UNUSED_RESULT Tagged< FreeSpace > Allocate(size_t size_in_bytes, size_t *node_size, AllocationOrigin origin) override
Definition free-list.cc:323
void UpdateCacheAfterAddition(FreeListCategoryType cat)
Definition free-list.h:372
void RemoveCategory(FreeListCategory *category) override
Definition free-list.cc:277
void UpdateCacheAfterRemoval(FreeListCategoryType cat)
Definition free-list.h:380
int next_nonempty_category[kNumberOfCategories+1]
Definition free-list.h:402
FreeListCategoryType SelectFreeListCategoryType(size_t size_in_bytes) override
Definition free-list.h:330
static constexpr unsigned int categories_min[kNumberOfCategories]
Definition free-list.h:325
V8_WARN_UNUSED_RESULT Tagged< FreeSpace > Allocate(size_t size_in_bytes, size_t *node_size, AllocationOrigin origin) override
Definition free-list.cc:223
static constexpr size_t kMaxBlockSize
Definition free-list.h:311
PageMetadata * GetPageForSize(size_t size_in_bytes) override
Definition free-list.cc:209
virtual FreeListCategoryType SelectFreeListCategoryType(size_t size_in_bytes)=0
virtual void ResetForNonBlackAllocatedPages()
Definition free-list.cc:452
static V8_EXPORT_PRIVATE std::unique_ptr< FreeList > CreateFreeList()
Definition free-list.cc:131
FreeList(int number_of_categories, size_t min_block_size)
Definition free-list.cc:126
const int number_of_categories_
Definition free-list.h:270
Tagged< FreeSpace > SearchForNodeInList(FreeListCategoryType type, size_t minimum_size, size_t *node_size)
Definition free-list.cc:155
V8_EXPORT_PRIVATE void EvictFreeListItems(PageMetadata *page)
Definition free-list.cc:471
virtual bool AddCategory(FreeListCategory *category)
Definition free-list.cc:486
FreeListCategory ** categories_
Definition free-list.h:274
void IncreaseAvailableBytes(size_t bytes)
Definition free-list.h:175
std::atomic< size_t > wasted_bytes_
Definition free-list.h:280
PageMetadata * GetPageForCategoryType(FreeListCategoryType type)
void ForAllFreeListCategories(FreeListCategoryType type, Callback callback)
Definition free-list.h:201
static V8_EXPORT_PRIVATE std::unique_ptr< FreeList > CreateFreeListForNewSpace()
Definition free-list.cc:135
virtual size_t Free(const WritableFreeSpace &free_space, FreeMode mode)
Definition free-list.cc:175
void DecreaseAvailableBytes(size_t bytes)
Definition free-list.h:176
virtual void Reset()
Definition free-list.cc:442
const FreeListCategoryType last_category_
Definition free-list.h:271
friend class FreeListCategory
Definition free-list.h:282
virtual V8_WARN_UNUSED_RESULT Tagged< FreeSpace > Allocate(size_t size_in_bytes, size_t *node_size, AllocationOrigin origin)=0
virtual V8_EXPORT_PRIVATE void RemoveCategory(FreeListCategory *category)
Definition free-list.cc:505
void RepairLists(Heap *heap)
Definition free-list.cc:481
void PrintCategories(FreeListCategoryType type)
Definition free-list.cc:528
Tagged< FreeSpace > TryFindNodeIn(FreeListCategoryType type, size_t minimum_size, size_t *node_size)
Definition free-list.cc:139
bool contains_map_value(Address raw_value) const
Definition slots-inl.h:36
void store_map(Tagged< Map > map) const
Definition slots-inl.h:58
static Tagged< HeapObject > FromAddress(Address address)
Isolate * isolate() const
Definition heap-inl.h:61
Tagged< Object > root(RootIndex index) const
Definition isolate.h:1265
static V8_INLINE MemoryChunk * FromHeapObject(Tagged< HeapObject > object)
static V8_INLINE PageMetadata * FromHeapObject(Tagged< HeapObject > o)
static V8_INLINE PageMetadata * FromAddress(Address addr)
V8_INLINE constexpr StorageType ptr() const
V8_INLINE constexpr bool is_null() const
Definition tagged.h:502
static V8_INLINE WritableFreeSpace ForNonExecutableMemory(base::Address addr, size_t size)
V8_INLINE WritableFreeSpace FreeRange(Address addr, size_t size)
BasePage * page
Definition sweeper.cc:218
int start
Node * node
static constexpr FreeListCategoryType kFirstCategory
Definition free-list.h:39
void PrintF(const char *format,...)
Definition utils.cc:39
int32_t FreeListCategoryType
Definition free-list.h:37
V8_EXPORT_PRIVATE FlagValues v8_flags
static constexpr Address kNullAddress
Definition v8-internal.h:53
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
static constexpr RelaxedLoadTag kRelaxedLoad
Definition globals.h:2909
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define DCHECK_GE(v1, v2)
Definition logging.h:488
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_LT(v1, v2)
Definition logging.h:489
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define USE(...)
Definition macros.h:293
wasm::ValueType type