Files
sdk/runtime/vm/virtual_memory_compressed.cc
T
Ryan Macnak 8f9113d9f1 [vm, gc] Try to handle virtual memory limitations on iOS.
iOS appears not to allow any single virtual memory allocation to exceed ~700MB.
iOS appears to limit the total amount of allocated virtual memory to amount of physical memory available, even virtual memory that is reserved by not committed.

So instead of trying to allocate the full 4GB region for compressed pointer, allocate the largest power-of-two that succeeds, and speculate that further allocations to into the same 4GB region.

TEST=none
Change-Id: Ib45f7ece59e1adb96d175ae861b984c0c6737549
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/210640
Reviewed-by: Siva Annamalai <asiva@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
2021-08-31 00:10:08 +00:00

151 lines
5.0 KiB
C++

// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "vm/virtual_memory_compressed.h"
#include "platform/utils.h"
#if defined(DART_COMPRESSED_HEAP)
namespace dart {
uword VirtualMemoryCompressedHeap::base_ = 0;
uword VirtualMemoryCompressedHeap::size_ = 0;
uint8_t* VirtualMemoryCompressedHeap::pages_ = nullptr;
uword VirtualMemoryCompressedHeap::minimum_free_page_id_ = 0;
Mutex* VirtualMemoryCompressedHeap::mutex_ = nullptr;
uint8_t PageMask(uword page_id) {
return static_cast<uint8_t>(1 << (page_id % 8));
}
bool VirtualMemoryCompressedHeap::IsPageUsed(uword page_id) {
if (page_id >= kCompressedHeapNumPages) return false;
return pages_[page_id / 8] & PageMask(page_id);
}
void VirtualMemoryCompressedHeap::SetPageUsed(uword page_id) {
ASSERT(page_id < kCompressedHeapNumPages);
pages_[page_id / 8] |= PageMask(page_id);
}
void VirtualMemoryCompressedHeap::ClearPageUsed(uword page_id) {
ASSERT(page_id < kCompressedHeapNumPages);
pages_[page_id / 8] &= ~PageMask(page_id);
}
void VirtualMemoryCompressedHeap::Init(void* compressed_heap_region,
size_t size) {
pages_ = new uint8_t[kCompressedHeapBitmapSize];
memset(pages_, 0, kCompressedHeapBitmapSize);
ASSERT(size > 0);
ASSERT(size <= kCompressedHeapSize);
for (intptr_t page_id = size / kCompressedHeapPageSize;
page_id < kCompressedHeapNumPages; page_id++) {
SetPageUsed(page_id);
}
base_ = reinterpret_cast<uword>(compressed_heap_region);
size_ = size;
ASSERT(base_ != 0);
ASSERT(size_ != 0);
ASSERT(size_ <= kCompressedHeapSize);
ASSERT(Utils::IsAligned(base_, kCompressedHeapPageSize));
ASSERT(Utils::IsAligned(size_, kCompressedHeapPageSize));
// base_ is not necessarily 4GB-aligned, because on some systems we can't make
// a large enough reservation to guarentee it. Instead, we have only the
// weaker property that all addresses in [base_, base_ + size_) have the same
// same upper 32 bits, which is what we really need for compressed pointers.
intptr_t mask = ~(kCompressedHeapAlignment - 1);
ASSERT((base_ & mask) == (base_ + size_ - 1 & mask));
mutex_ = new Mutex(NOT_IN_PRODUCT("compressed_heap_mutex"));
}
void VirtualMemoryCompressedHeap::Cleanup() {
delete[] pages_;
delete mutex_;
base_ = 0;
size_ = 0;
pages_ = nullptr;
minimum_free_page_id_ = 0;
mutex_ = nullptr;
}
void* VirtualMemoryCompressedHeap::GetRegion() {
return reinterpret_cast<void*>(base_);
}
MemoryRegion VirtualMemoryCompressedHeap::Allocate(intptr_t size,
intptr_t alignment) {
ASSERT(alignment <= kCompressedHeapAlignment);
const intptr_t allocated_size = Utils::RoundUp(size, kCompressedHeapPageSize);
uword pages = allocated_size / kCompressedHeapPageSize;
uword page_alignment = alignment > kCompressedHeapPageSize
? alignment / kCompressedHeapPageSize
: 1;
MutexLocker ml(mutex_);
// Find a gap with enough empty pages, using the bitmap. Note that reading
// outside the bitmap range always returns 0, so this loop will terminate.
uword page_id = Utils::RoundUp(minimum_free_page_id_, page_alignment);
for (uword gap = 0;;) {
if (IsPageUsed(page_id)) {
gap = 0;
page_id = Utils::RoundUp(page_id + 1, page_alignment);
} else {
++gap;
if (gap >= pages) {
page_id += 1 - gap;
break;
}
++page_id;
}
}
ASSERT(page_id % page_alignment == 0);
// Make sure we're not trying to allocate past the end of the heap.
uword end = page_id + pages;
if (end > kCompressedHeapSize / kCompressedHeapPageSize) {
return MemoryRegion();
}
// Mark all the pages in the bitmap as allocated.
for (uword i = page_id; i < end; ++i) {
ASSERT(!IsPageUsed(i));
SetPageUsed(i);
}
// Find the next free page, to speed up subsequent allocations.
while (IsPageUsed(minimum_free_page_id_)) {
++minimum_free_page_id_;
}
uword address = base_ + page_id * kCompressedHeapPageSize;
ASSERT(Utils::IsAligned(address, kCompressedHeapPageSize));
return MemoryRegion(reinterpret_cast<void*>(address), allocated_size);
}
void VirtualMemoryCompressedHeap::Free(void* address, intptr_t size) {
uword start = reinterpret_cast<uword>(address);
ASSERT(Utils::IsAligned(start, kCompressedHeapPageSize));
ASSERT(Utils::IsAligned(size, kCompressedHeapPageSize));
MutexLocker ml(mutex_);
ASSERT(start >= base_);
uword page_id = (start - base_) / kCompressedHeapPageSize;
uword end = page_id + size / kCompressedHeapPageSize;
for (uword i = page_id; i < end; ++i) {
ClearPageUsed(i);
}
if (page_id < minimum_free_page_id_) {
minimum_free_page_id_ = page_id;
}
}
bool VirtualMemoryCompressedHeap::Contains(void* address) {
return (reinterpret_cast<uword>(address) - base_) < size_;
}
} // namespace dart
#endif // defined(DART_COMPRESSED_HEAP)