v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
region-allocator.cc
Go to the documentation of this file.
1// Copyright 2018 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 <iterator>
8
9#include "src/base/bits.h"
10#include "src/base/logging.h"
11#include "src/base/macros.h"
12
13namespace v8 {
14namespace base {
15
16// If |free_size| < |region_size| * |kMaxLoadFactorForRandomization| stop trying
17// to randomize region allocation.
18constexpr double kMaxLoadFactorForRandomization = 0.40;
19
20// Max number of attempts to allocate page at random address.
21constexpr int kMaxRandomizationAttempts = 3;
22
24 size_t memory_region_size, size_t page_size)
25 : whole_region_(memory_region_begin, memory_region_size,
26 RegionState::kFree),
27 region_size_in_pages_(size() / page_size),
28 max_load_for_randomization_(
29 static_cast<size_t>(size() * kMaxLoadFactorForRandomization)),
30 free_size_(0),
31 page_size_(page_size) {
32 CHECK_LT(begin(), end());
36
37 // Initial region.
38 Region* region = new Region(whole_region_);
39
40 all_regions_.insert(region);
41
42 FreeListAddRegion(region);
43}
44
46 // TODO(chromium:1218005) either (D)CHECK that all allocated regions have
47 // been freed again (and thus merged into a single region) or do that now.
48 for (Region* region : all_regions_) {
49 delete region;
50 }
51}
52
53RegionAllocator::AllRegionsSet::iterator RegionAllocator::FindRegion(
54 Address address) {
55 if (!whole_region_.contains(address)) return all_regions_.end();
56
57 Region key(address, 0, RegionState::kFree);
58 AllRegionsSet::iterator iter = all_regions_.upper_bound(&key);
59 // Regions in |all_regions_| are compared by end() values and key's end()
60 // points exactly to the address we are querying, so the upper_bound will
61 // find the region whose |end()| is greater than the requested address.
62 DCHECK_NE(iter, all_regions_.end());
63 DCHECK((*iter)->contains(address));
64 return iter;
65}
66
68 free_size_ += region->size();
69 free_regions_.insert(region);
70}
71
74 auto iter = free_regions_.lower_bound(&key);
75 return iter == free_regions_.end() ? nullptr : *iter;
76}
77
79 DCHECK(region->is_free());
80 auto iter = free_regions_.find(region);
81 DCHECK_NE(iter, free_regions_.end());
82 DCHECK_EQ(region, *iter);
83 DCHECK_LE(region->size(), free_size_);
84 free_size_ -= region->size();
85 free_regions_.erase(iter);
86}
87
89 size_t new_size) {
90 DCHECK(IsAligned(new_size, page_size_));
91 DCHECK_NE(new_size, 0);
92 DCHECK_GT(region->size(), new_size);
93
94 if (on_split_) on_split_(region->begin(), new_size);
95
96 // Create new region and put it to the lists after the |region|.
97 DCHECK(!region->is_excluded());
98 RegionState state = region->state();
99 Region* new_region =
100 new Region(region->begin() + new_size, region->size() - new_size, state);
101 if (state == RegionState::kFree) {
102 // Remove region from the free list before updating it's size.
103 FreeListRemoveRegion(region);
104 }
105 region->set_size(new_size);
106
107 all_regions_.insert(new_region);
108
109 if (state == RegionState::kFree) {
110 FreeListAddRegion(region);
111 FreeListAddRegion(new_region);
112 }
113 return new_region;
114}
115
116void RegionAllocator::Merge(AllRegionsSet::iterator prev_iter,
117 AllRegionsSet::iterator next_iter) {
118 Region* prev = *prev_iter;
119 Region* next = *next_iter;
120 DCHECK_EQ(prev->end(), next->begin());
121
122 if (on_merge_) on_merge_(prev->begin(), prev->size() + next->size());
123
124 prev->set_size(prev->size() + next->size());
125
126 all_regions_.erase(next_iter); // prev_iter stays valid.
127
128 // The |next| region must already not be in the free list.
129 DCHECK_EQ(free_regions_.find(next), free_regions_.end());
130 delete next;
131}
132
134 DCHECK_NE(size, 0);
136
137 Region* region = FreeListFindRegion(size);
138 if (region == nullptr) return kAllocationFailure;
139
140 if (region->size() != size) {
141 Split(region, size);
142 }
143 DCHECK(IsAligned(region->begin(), page_size_));
144 DCHECK_EQ(region->size(), size);
145
146 // Mark region as used.
147 FreeListRemoveRegion(region);
148 region->set_state(RegionState::kAllocated);
149 return region->begin();
150}
151
153 RandomNumberGenerator* rng, size_t size) {
155 // There is enough free space for trying to randomize the address.
156 size_t random = 0;
157
158 for (int i = 0; i < kMaxRandomizationAttempts; i++) {
159 rng->NextBytes(&random, sizeof(random));
160 size_t random_offset = page_size_ * (random % region_size_in_pages_);
161 Address address = begin() + random_offset;
162 if (AllocateRegionAt(address, size, RegionState::kAllocated)) {
163 return address;
164 }
165 }
166 // Fall back to free list allocation.
167 }
168 return AllocateRegion(size);
169}
170
171bool RegionAllocator::AllocateRegionAt(Address requested_address, size_t size,
172 RegionState region_state) {
173 DCHECK(IsAligned(requested_address, page_size_));
174 DCHECK_NE(size, 0);
176 DCHECK_NE(region_state, RegionState::kFree);
177
178 Address requested_end = requested_address + size;
179 DCHECK_LE(requested_end, end());
180
181 Region* region;
182 {
183 AllRegionsSet::iterator region_iter = FindRegion(requested_address);
184 if (region_iter == all_regions_.end()) {
185 return false;
186 }
187 region = *region_iter;
188 }
189 if (!region->is_free() || region->end() < requested_end) {
190 return false;
191 }
192 // Found free region that includes the requested one.
193 if (region->begin() != requested_address) {
194 // Split the region at the |requested_address| boundary.
195 size_t new_size = requested_address - region->begin();
196 DCHECK(IsAligned(new_size, page_size_));
197 region = Split(region, new_size);
198 }
199 if (region->end() != requested_end) {
200 // Split the region at the |requested_end| boundary.
201 Split(region, size);
202 }
203 DCHECK_EQ(region->begin(), requested_address);
204 DCHECK_EQ(region->size(), size);
205
206 // Mark region as used.
207 FreeListRemoveRegion(region);
208 region->set_state(region_state);
209 return true;
210}
211
213 size_t size, size_t alignment) {
215 DCHECK(IsAligned(alignment, page_size_));
216 DCHECK_GE(alignment, page_size_);
217
218 const size_t padded_size = size + alignment - page_size_;
219 Region* region = FreeListFindRegion(padded_size);
220 if (region == nullptr) {
221 // In case we are out of space we might still fit an allocation without
222 // padding and the result might still satisfy our alignment requirements.
223 region = FreeListFindRegion(size);
224 }
225 if (region == nullptr) return kAllocationFailure;
226
227 if (!IsAligned(region->begin(), alignment)) {
228 size_t start = RoundUp(region->begin(), alignment);
229 if (start + size > region->end()) {
230 return kAllocationFailure;
231 }
232 region = Split(region, start - region->begin());
233 DCHECK_EQ(region->begin(), start);
234 DCHECK(IsAligned(region->begin(), alignment));
235 }
236
237 if (region->size() != size) {
238 Split(region, size);
239 }
240 DCHECK(IsAligned(region->begin(), alignment));
241 DCHECK_EQ(region->size(), size);
242
243 // Mark region as used.
244 FreeListRemoveRegion(region);
245 region->set_state(RegionState::kAllocated);
246 return region->begin();
247}
248
250 size_t size,
251 size_t alignment) {
252 DCHECK(IsAligned(alignment, page_size()));
253 DCHECK(IsAligned(hint, alignment));
254
255 if (hint && contains(hint, size)) {
256 if (AllocateRegionAt(hint, size)) {
257 return hint;
258 }
259 }
260
261 Address address;
262 if (alignment <= page_size()) {
263 // TODO(chromium:1218005): Consider using randomized version here.
264 address = AllocateRegion(size);
265 } else {
266 address = AllocateAlignedRegion(size, alignment);
267 }
268
269 return address;
270}
271
272size_t RegionAllocator::TrimRegion(Address address, size_t new_size) {
273 DCHECK(IsAligned(new_size, page_size_));
274
275 AllRegionsSet::iterator region_iter = FindRegion(address);
276 if (region_iter == all_regions_.end()) {
277 return 0;
278 }
279 Region* region = *region_iter;
280 if (region->begin() != address || !region->is_allocated()) {
281 return 0;
282 }
283
284 // The region must not be in the free list.
285 DCHECK_EQ(free_regions_.find(*region_iter), free_regions_.end());
286
287 if (new_size > 0) {
288 region = Split(region, new_size);
289 ++region_iter;
290 }
291 size_t size = region->size();
292 region->set_state(RegionState::kFree);
293
294 // Merge current region with the surrounding ones if they are free.
295 if (region->end() != whole_region_.end()) {
296 // There must be a range after the current one.
297 AllRegionsSet::iterator next_iter = std::next(region_iter);
298 DCHECK_NE(next_iter, all_regions_.end());
299 if ((*next_iter)->is_free()) {
300 // |next| region object will be deleted during merge, remove it from
301 // the free list.
302 FreeListRemoveRegion(*next_iter);
303 Merge(region_iter, next_iter);
304 }
305 }
306 if (new_size == 0 && region->begin() != whole_region_.begin()) {
307 // There must be a range before the current one.
308 AllRegionsSet::iterator prev_iter = std::prev(region_iter);
309 DCHECK_NE(prev_iter, all_regions_.end());
310 if ((*prev_iter)->is_free()) {
311 // |prev| region's size will change, we'll have to re-insert it into
312 // the proper place of the free list.
313 FreeListRemoveRegion(*prev_iter);
314 Merge(prev_iter, region_iter);
315 // |prev| region becomes the current region.
316 region_iter = prev_iter;
317 region = *region_iter;
318 }
319 }
320 FreeListAddRegion(region);
321 return size;
322}
323
325 AllRegionsSet::iterator region_iter = FindRegion(address);
326 if (region_iter == all_regions_.end()) {
327 return 0;
328 }
329 Region* region = *region_iter;
330 if (region->begin() != address || region->is_free()) {
331 return 0;
332 }
333 return region->size();
334}
335
336bool RegionAllocator::IsFree(Address address, size_t size) {
337 CHECK(contains(address, size));
338 AllRegionsSet::iterator region_iter = FindRegion(address);
339 if (region_iter == all_regions_.end()) {
340 return true;
341 }
342 Region* region = *region_iter;
343 return region->is_free() && region->contains(address, size);
344}
345
346namespace {
347const char* RegionStateToString(RegionAllocator::RegionState state) {
348 switch (state) {
350 return "free";
352 return "excluded";
354 return "used";
355 default:
356 UNREACHABLE();
357 }
358}
359} // namespace
360
361void RegionAllocator::Region::Print(std::ostream& os) const {
362 std::ios::fmtflags flags = os.flags(std::ios::hex | std::ios::showbase);
363 os << "[" << begin() << ", " << end() << "), size: " << size();
364 os << ", " << RegionStateToString(state_);
365 os.flags(flags);
366}
367
368void RegionAllocator::Print(std::ostream& os) const {
369 std::ios::fmtflags flags = os.flags(std::ios::hex | std::ios::showbase);
370 os << "RegionAllocator: [" << begin() << ", " << end() << ")";
371 os << "\nsize: " << size();
372 os << "\nfree_size: " << free_size();
373 os << "\npage_size: " << page_size_;
374
375 os << "\nall regions: ";
376 for (const Region* region : all_regions_) {
377 os << "\n ";
378 region->Print(os);
379 }
380
381 os << "\nfree regions: ";
382 for (const Region* region : free_regions_) {
383 os << "\n ";
384 region->Print(os);
385 }
386 os << "\n";
387 os.flags(flags);
388}
389
390} // namespace base
391} // namespace v8
void set_size(size_t size)
void NextBytes(void *buffer, size_t buflen)
void Print(std::ostream &os) const
Region * FreeListFindRegion(size_t size)
RegionAllocator(Address address, size_t size, size_t page_size)
void FreeListRemoveRegion(Region *region)
void Print(std::ostream &os) const
Address AllocateRegion(size_t size)
bool AllocateRegionAt(Address requested_address, size_t size, RegionState region_state=RegionState::kAllocated)
bool contains(Address address) const
const size_t max_load_for_randomization_
SplitMergeCallback on_split_
bool IsFree(Address address, size_t size)
size_t TrimRegion(Address address, size_t new_size)
void Merge(AllRegionsSet::iterator prev_iter, AllRegionsSet::iterator next_iter)
SplitMergeCallback on_merge_
Address AllocateAlignedRegion(size_t size, size_t alignment)
size_t CheckRegion(Address address)
static constexpr Address kAllocationFailure
Region * Split(Region *region, size_t new_size)
std::set< Region *, SizeAddressOrder > free_regions_
AllRegionsSet::iterator FindRegion(Address address)
void FreeListAddRegion(Region *region)
int start
refactor address components for immediate indexing make OptimizeMaglevOnNextCall optimize to turbofan instead of maglev filter for tracing turbofan compilation nullptr
constexpr bool IsPowerOfTwo(T value)
Definition bits.h:187
constexpr int kMaxRandomizationAttempts
constexpr double kMaxLoadFactorForRandomization
#define UNREACHABLE()
Definition logging.h:67
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define CHECK(condition)
Definition logging.h:124
#define CHECK_LT(lhs, rhs)
#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_EQ(v1, v2)
Definition logging.h:485
#define DCHECK_GT(v1, v2)
Definition logging.h:487
constexpr T RoundUp(T x, intptr_t m)
Definition macros.h:387
constexpr bool IsAligned(T value, U alignment)
Definition macros.h:403
std::unique_ptr< ValueMirror > key