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 <algorithm>
8
10#include "src/base/bits.h"
14
15namespace cppgc {
16namespace internal {
17
18namespace {
19uint32_t BucketIndexForSize(uint32_t size) {
22}
23} // namespace
24
26 public:
27 static Entry& CreateAt(void* memory, size_t size) {
28 // Make sure the freelist header is writable. SET_MEMORY_ACCESSIBLE is not
29 // needed as we write the whole payload of Entry.
30 ASAN_UNPOISON_MEMORY_REGION(memory, sizeof(Entry));
31 return *new (memory) Entry(size);
32 }
33
34 Entry* Next() const { return next_; }
35 void SetNext(Entry* next) { next_ = next; }
36
37 void Link(Entry** previous_next) {
38 next_ = *previous_next;
39 *previous_next = this;
40 }
41 void Unlink(Entry** previous_next) {
42 *previous_next = next_;
43 next_ = nullptr;
44 }
45
46 private:
47 explicit Entry(size_t size) : HeapObjectHeader(size, kFreeListGCInfoIndex) {
48 static_assert(sizeof(Entry) == kFreeListEntrySize, "Sizes must match");
49 }
50
51 Entry* next_ = nullptr;
52};
53
55
57 : free_list_heads_(std::move(other.free_list_heads_)),
58 free_list_tails_(std::move(other.free_list_tails_)),
59 biggest_free_list_index_(std::move(other.biggest_free_list_index_)) {
60 other.Clear();
61}
62
64 Clear();
65 Append(std::move(other));
66 DCHECK(other.IsEmpty());
67 return *this;
68}
69
71
72std::pair<Address, Address> FreeList::AddReturningUnusedBounds(Block block) {
73 const size_t size = block.size;
74 DCHECK_GT(kPageSize, size);
75 DCHECK_LE(sizeof(HeapObjectHeader), size);
76
77 if (size < sizeof(Entry)) {
78 // Create wasted entry. This can happen when an almost emptied linear
79 // allocation buffer is returned to the freelist.
80 // This could be SET_MEMORY_ACCESSIBLE. Since there's no payload, the next
81 // operating overwrites the memory completely, and we can thus avoid
82 // zeroing it out.
83 auto& filler = Filler::CreateAt(block.address, size);
84 USE(filler);
85 DCHECK_EQ(reinterpret_cast<Address>(block.address) + size,
86 filler.ObjectEnd());
87 DCHECK_EQ(reinterpret_cast<Address>(&filler + 1), filler.ObjectEnd());
88 return {reinterpret_cast<Address>(&filler + 1),
89 reinterpret_cast<Address>(&filler + 1)};
90 }
91
92 Entry& entry = Entry::CreateAt(block.address, size);
93 const size_t index = BucketIndexForSize(static_cast<uint32_t>(size));
94 entry.Link(&free_list_heads_[index]);
96 if (!entry.Next()) {
97 free_list_tails_[index] = &entry;
98 }
99 DCHECK_EQ(entry.ObjectEnd(), reinterpret_cast<Address>(&entry) + size);
100 return {reinterpret_cast<Address>(&entry + 1),
101 reinterpret_cast<Address>(&entry) + size};
102}
103
105 DCHECK_NE(this, &other);
106#if DEBUG
107 const size_t expected_size = Size() + other.Size();
108#endif
109 // Newly created entries get added to the head.
110 for (size_t index = 0; index < free_list_tails_.size(); ++index) {
111 Entry* other_tail = other.free_list_tails_[index];
112 Entry*& this_head = this->free_list_heads_[index];
113 if (other_tail) {
114 other_tail->SetNext(this_head);
115 if (!this_head) {
116 this->free_list_tails_[index] = other_tail;
117 }
118 this_head = other.free_list_heads_[index];
119 other.free_list_heads_[index] = nullptr;
120 other.free_list_tails_[index] = nullptr;
121 }
122 }
123
125 std::max(biggest_free_list_index_, other.biggest_free_list_index_);
126 other.biggest_free_list_index_ = 0;
127#if DEBUG
128 DCHECK_EQ(expected_size, Size());
129#endif
130 DCHECK(other.IsEmpty());
131}
132
133FreeList::Block FreeList::Allocate(size_t allocation_size) {
134 // Try reusing a block from the largest bin. The underlying reasoning
135 // being that we want to amortize this slow allocation call by carving
136 // off as a large a free block as possible in one go; a block that will
137 // service this block and let following allocations be serviced quickly
138 // by bump allocation.
139 // bucket_size represents minimal size of entries in a bucket.
140 size_t bucket_size = static_cast<size_t>(1) << biggest_free_list_index_;
141 size_t index = biggest_free_list_index_;
142 for (; index > 0; --index, bucket_size >>= 1) {
143 DCHECK(IsConsistent(index));
144 Entry* entry = free_list_heads_[index];
145 if (allocation_size > bucket_size) {
146 // Final bucket candidate; check initial entry if it is able
147 // to service this allocation. Do not perform a linear scan,
148 // as it is considered too costly.
149 if (!entry || entry->AllocatedSize() < allocation_size) break;
150 }
151 if (entry) {
152 if (!entry->Next()) {
153 DCHECK_EQ(entry, free_list_tails_[index]);
154 free_list_tails_[index] = nullptr;
155 }
156 entry->Unlink(&free_list_heads_[index]);
158 return {entry, entry->AllocatedSize()};
159 }
160 }
162 return {nullptr, 0u};
163}
164
166 std::fill(free_list_heads_.begin(), free_list_heads_.end(), nullptr);
167 std::fill(free_list_tails_.begin(), free_list_tails_.end(), nullptr);
169}
170
171size_t FreeList::Size() const {
172 size_t size = 0;
173 for (auto* entry : free_list_heads_) {
174 while (entry) {
175 size += entry->AllocatedSize();
176 entry = entry->Next();
177 }
178 }
179 return size;
180}
181
182bool FreeList::IsEmpty() const {
183 return std::all_of(free_list_heads_.cbegin(), free_list_heads_.cend(),
184 [](const auto* entry) { return !entry; });
185}
186
188 for (Entry* list : free_list_heads_) {
189 for (Entry* entry = list; entry; entry = entry->Next()) {
190 if (entry <= block.address &&
191 (reinterpret_cast<Address>(block.address) + block.size <=
192 reinterpret_cast<Address>(entry) + entry->AllocatedSize()))
193 return true;
194 }
195 }
196 return false;
197}
198
199bool FreeList::IsConsistent(size_t index) const {
200 // Check that freelist head and tail pointers are consistent, i.e.
201 // - either both are nulls (no entries in the bucket);
202 // - or both are non-nulls and the tail points to the end.
203 return (!free_list_heads_[index] && !free_list_tails_[index]) ||
205 !free_list_tails_[index]->Next());
206}
207
209 HeapStatistics::FreeListStatistics& free_list_stats) {
210 std::vector<size_t>& bucket_size = free_list_stats.bucket_size;
211 std::vector<size_t>& free_count = free_list_stats.free_count;
212 std::vector<size_t>& free_size = free_list_stats.free_size;
213 DCHECK(bucket_size.empty());
214 DCHECK(free_count.empty());
215 DCHECK(free_size.empty());
216 for (size_t i = 0; i < kPageSizeLog2; ++i) {
217 size_t entry_count = 0;
218 size_t entry_size = 0;
219 for (Entry* entry = free_list_heads_[i]; entry; entry = entry->Next()) {
220 ++entry_count;
221 entry_size += entry->AllocatedSize();
222 }
223 bucket_size.push_back(static_cast<size_t>(1) << i);
224 free_count.push_back(entry_count);
225 free_size.push_back(entry_size);
226 }
227}
228
229} // namespace internal
230} // namespace cppgc
#define ASAN_UNPOISON_MEMORY_REGION(start, size)
Definition asan.h:71
static Filler & CreateAt(void *memory, size_t size)
Definition free-list.h:76
void Link(Entry **previous_next)
Definition free-list.cc:37
static Entry & CreateAt(void *memory, size_t size)
Definition free-list.cc:27
void Unlink(Entry **previous_next)
Definition free-list.cc:41
std::array< Entry *, kPageSizeLog2 > free_list_tails_
Definition free-list.h:71
bool IsConsistent(size_t) const
Definition free-list.cc:199
std::array< Entry *, kPageSizeLog2 > free_list_heads_
Definition free-list.h:70
void CollectStatistics(HeapStatistics::FreeListStatistics &)
Definition free-list.cc:208
bool ContainsForTesting(Block) const
Definition free-list.cc:187
std::pair< Address, Address > AddReturningUnusedBounds(Block)
Definition free-list.cc:72
void Append(FreeList &&)
Definition free-list.cc:104
FreeList & operator=(const FreeList &)=delete
OptionalOpIndex index
constexpr size_t kPageSizeLog2
Definition globals.h:41
constexpr size_t kPageSize
Definition globals.h:42
uint8_t * Address
Definition globals.h:17
constexpr GCInfoIndex kFreeListGCInfoIndex
Definition globals.h:48
constexpr size_t kFreeListEntrySize
Definition globals.h:49
uint32_t RoundDownToPowerOfTwo32(uint32_t value)
Definition bits.h:265
constexpr int WhichPowerOfTwo(T value)
Definition bits.h:195
#define V8_NOEXCEPT
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define DCHECK_GT(v1, v2)
Definition logging.h:487
#define USE(...)
Definition macros.h:293