// 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" #include "vm/lockers.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(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 / kCompressedPageSize; page_id < kCompressedHeapNumPages; page_id++) { SetPageUsed(page_id); } base_ = reinterpret_cast(compressed_heap_region); size_ = size; ASSERT(base_ != 0); ASSERT(size_ != 0); ASSERT(size_ <= kCompressedHeapSize); ASSERT(Utils::IsAligned(base_, kCompressedPageSize)); ASSERT(Utils::IsAligned(size_, kCompressedPageSize)); // base_ is not necessarily 4GB-aligned, because on some systems we can't make // a large enough reservation to guarantee 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(); } 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(base_); } MemoryRegion VirtualMemoryCompressedHeap::Allocate(intptr_t size, intptr_t alignment) { ASSERT(alignment <= kCompressedHeapAlignment); const intptr_t allocated_size = Utils::RoundUp(size, kCompressedPageSize); uword pages = allocated_size / kCompressedPageSize; uword page_alignment = alignment > kCompressedPageSize ? alignment / kCompressedPageSize : 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 / kCompressedPageSize) { 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 * kCompressedPageSize; ASSERT(Utils::IsAligned(address, kCompressedPageSize)); return MemoryRegion(reinterpret_cast(address), allocated_size); } void VirtualMemoryCompressedHeap::Free(void* address, intptr_t size) { uword start = reinterpret_cast(address); ASSERT(Utils::IsAligned(start, kCompressedPageSize)); ASSERT(Utils::IsAligned(size, kCompressedPageSize)); MutexLocker ml(mutex_); ASSERT(start >= base_); uword page_id = (start - base_) / kCompressedPageSize; uword end = page_id + size / kCompressedPageSize; 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(address) - base_) < size_; } } // namespace dart #endif // defined(DART_COMPRESSED_HEAP)