blob: baa70162fd925a23fb830b0670bb54dbf704bc0f [file] [log] [blame]
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/zone/accounting-allocator.h"
#include <memory>
#include "src/base/bounded-page-allocator.h"
#include "src/base/logging.h"
#include "src/base/macros.h"
#include "src/base/platform/wrappers.h"
#include "src/utils/allocation.h"
#include "src/zone/zone-compression.h"
#include "src/zone/zone-segment.h"
namespace v8 {
namespace internal {
// These definitions are here in order to please the linker, which in debug mode
// sometimes requires static constants to be defined in .cc files.
const size_t ZoneCompression::kReservationSize;
const size_t ZoneCompression::kReservationAlignment;
namespace {
static constexpr size_t kZonePageSize = 256 * KB;
VirtualMemory ReserveAddressSpace(v8::PageAllocator* platform_allocator) {
DCHECK(IsAligned(ZoneCompression::kReservationSize,
platform_allocator->AllocatePageSize()));
void* hint = reinterpret_cast<void*>(RoundDown(
reinterpret_cast<uintptr_t>(platform_allocator->GetRandomMmapAddr()),
ZoneCompression::kReservationAlignment));
VirtualMemory memory(platform_allocator, ZoneCompression::kReservationSize,
hint, ZoneCompression::kReservationAlignment);
if (memory.IsReserved()) {
CHECK(IsAligned(memory.address(), ZoneCompression::kReservationAlignment));
return memory;
}
FATAL(
"Fatal process out of memory: Failed to reserve memory for compressed "
"zones");
UNREACHABLE();
}
std::unique_ptr<v8::base::BoundedPageAllocator> CreateBoundedAllocator(
v8::PageAllocator* platform_allocator, Address reservation_start) {
CHECK(reservation_start);
CHECK(IsAligned(reservation_start, ZoneCompression::kReservationAlignment));
auto allocator = std::make_unique<v8::base::BoundedPageAllocator>(
platform_allocator, reservation_start, ZoneCompression::kReservationSize,
kZonePageSize);
// Exclude first page from allocation to ensure that accesses through
// decompressed null pointer will seg-fault.
allocator->AllocatePagesAt(reservation_start, kZonePageSize,
v8::PageAllocator::kNoAccess);
return allocator;
}
} // namespace
AccountingAllocator::AccountingAllocator() {
if (COMPRESS_ZONES_BOOL) {
v8::PageAllocator* platform_page_allocator = GetPlatformPageAllocator();
VirtualMemory memory = ReserveAddressSpace(platform_page_allocator);
reserved_area_ = std::make_unique<VirtualMemory>(std::move(memory));
bounded_page_allocator_ = CreateBoundedAllocator(platform_page_allocator,
reserved_area_->address());
}
}
AccountingAllocator::~AccountingAllocator() = default;
Segment* AccountingAllocator::AllocateSegment(size_t bytes,
bool supports_compression) {
void* memory;
if (COMPRESS_ZONES_BOOL && supports_compression) {
bytes = RoundUp(bytes, kZonePageSize);
memory = AllocatePages(bounded_page_allocator_.get(), nullptr, bytes,
kZonePageSize, PageAllocator::kReadWrite);
} else {
memory = AllocWithRetry(bytes);
}
if (memory == nullptr) return nullptr;
size_t current =
current_memory_usage_.fetch_add(bytes, std::memory_order_relaxed) + bytes;
size_t max = max_memory_usage_.load(std::memory_order_relaxed);
while (current > max && !max_memory_usage_.compare_exchange_weak(
max, current, std::memory_order_relaxed)) {
// {max} was updated by {compare_exchange_weak}; retry.
}
DCHECK_LE(sizeof(Segment), bytes);
return new (memory) Segment(bytes);
}
void AccountingAllocator::ReturnSegment(Segment* segment,
bool supports_compression) {
segment->ZapContents();
size_t segment_size = segment->total_size();
current_memory_usage_.fetch_sub(segment_size, std::memory_order_relaxed);
segment->ZapHeader();
if (COMPRESS_ZONES_BOOL && supports_compression) {
CHECK(FreePages(bounded_page_allocator_.get(), segment, segment_size));
} else {
base::Free(segment);
}
}
} // namespace internal
} // namespace v8