| // 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 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(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) |