diff options
Diffstat (limited to 'src/3rdparty/v8/src/spaces-inl.h')
-rw-r--r-- | src/3rdparty/v8/src/spaces-inl.h | 529 |
1 files changed, 529 insertions, 0 deletions
diff --git a/src/3rdparty/v8/src/spaces-inl.h b/src/3rdparty/v8/src/spaces-inl.h new file mode 100644 index 0000000..070f970 --- /dev/null +++ b/src/3rdparty/v8/src/spaces-inl.h @@ -0,0 +1,529 @@ +// Copyright 2006-2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_SPACES_INL_H_ +#define V8_SPACES_INL_H_ + +#include "isolate.h" +#include "spaces.h" +#include "v8memory.h" + +namespace v8 { +namespace internal { + + +// ----------------------------------------------------------------------------- +// PageIterator + +bool PageIterator::has_next() { + return prev_page_ != stop_page_; +} + + +Page* PageIterator::next() { + ASSERT(has_next()); + prev_page_ = (prev_page_ == NULL) + ? space_->first_page_ + : prev_page_->next_page(); + return prev_page_; +} + + +// ----------------------------------------------------------------------------- +// Page + +Page* Page::next_page() { + return heap_->isolate()->memory_allocator()->GetNextPage(this); +} + + +Address Page::AllocationTop() { + PagedSpace* owner = heap_->isolate()->memory_allocator()->PageOwner(this); + return owner->PageAllocationTop(this); +} + + +Address Page::AllocationWatermark() { + PagedSpace* owner = heap_->isolate()->memory_allocator()->PageOwner(this); + if (this == owner->AllocationTopPage()) { + return owner->top(); + } + return address() + AllocationWatermarkOffset(); +} + + +uint32_t Page::AllocationWatermarkOffset() { + return static_cast<uint32_t>((flags_ & kAllocationWatermarkOffsetMask) >> + kAllocationWatermarkOffsetShift); +} + + +void Page::SetAllocationWatermark(Address allocation_watermark) { + if ((heap_->gc_state() == Heap::SCAVENGE) && IsWatermarkValid()) { + // When iterating intergenerational references during scavenge + // we might decide to promote an encountered young object. + // We will allocate a space for such an object and put it + // into the promotion queue to process it later. + // If space for object was allocated somewhere beyond allocation + // watermark this might cause garbage pointers to appear under allocation + // watermark. To avoid visiting them during dirty regions iteration + // which might be still in progress we store a valid allocation watermark + // value and mark this page as having an invalid watermark. + SetCachedAllocationWatermark(AllocationWatermark()); + InvalidateWatermark(true); + } + + flags_ = (flags_ & kFlagsMask) | + Offset(allocation_watermark) << kAllocationWatermarkOffsetShift; + ASSERT(AllocationWatermarkOffset() + == static_cast<uint32_t>(Offset(allocation_watermark))); +} + + +void Page::SetCachedAllocationWatermark(Address allocation_watermark) { + mc_first_forwarded = allocation_watermark; +} + + +Address Page::CachedAllocationWatermark() { + return mc_first_forwarded; +} + + +uint32_t Page::GetRegionMarks() { + return dirty_regions_; +} + + +void Page::SetRegionMarks(uint32_t marks) { + dirty_regions_ = marks; +} + + +int Page::GetRegionNumberForAddress(Address addr) { + // Each page is divided into 256 byte regions. Each region has a corresponding + // dirty mark bit in the page header. Region can contain intergenerational + // references iff its dirty mark is set. + // A normal 8K page contains exactly 32 regions so all region marks fit + // into 32-bit integer field. To calculate a region number we just divide + // offset inside page by region size. + // A large page can contain more then 32 regions. But we want to avoid + // additional write barrier code for distinguishing between large and normal + // pages so we just ignore the fact that addr points into a large page and + // calculate region number as if addr pointed into a normal 8K page. This way + // we get a region number modulo 32 so for large pages several regions might + // be mapped to a single dirty mark. + ASSERT_PAGE_ALIGNED(this->address()); + STATIC_ASSERT((kPageAlignmentMask >> kRegionSizeLog2) < kBitsPerInt); + + // We are using masking with kPageAlignmentMask instead of Page::Offset() + // to get an offset to the beginning of 8K page containing addr not to the + // beginning of actual page which can be bigger then 8K. + intptr_t offset_inside_normal_page = OffsetFrom(addr) & kPageAlignmentMask; + return static_cast<int>(offset_inside_normal_page >> kRegionSizeLog2); +} + + +uint32_t Page::GetRegionMaskForAddress(Address addr) { + return 1 << GetRegionNumberForAddress(addr); +} + + +uint32_t Page::GetRegionMaskForSpan(Address start, int length_in_bytes) { + uint32_t result = 0; + if (length_in_bytes >= kPageSize) { + result = kAllRegionsDirtyMarks; + } else if (length_in_bytes > 0) { + int start_region = GetRegionNumberForAddress(start); + int end_region = + GetRegionNumberForAddress(start + length_in_bytes - kPointerSize); + uint32_t start_mask = (~0) << start_region; + uint32_t end_mask = ~((~1) << end_region); + result = start_mask & end_mask; + // if end_region < start_region, the mask is ored. + if (result == 0) result = start_mask | end_mask; + } +#ifdef DEBUG + if (FLAG_enable_slow_asserts) { + uint32_t expected = 0; + for (Address a = start; a < start + length_in_bytes; a += kPointerSize) { + expected |= GetRegionMaskForAddress(a); + } + ASSERT(expected == result); + } +#endif + return result; +} + + +void Page::MarkRegionDirty(Address address) { + SetRegionMarks(GetRegionMarks() | GetRegionMaskForAddress(address)); +} + + +bool Page::IsRegionDirty(Address address) { + return GetRegionMarks() & GetRegionMaskForAddress(address); +} + + +void Page::ClearRegionMarks(Address start, Address end, bool reaches_limit) { + int rstart = GetRegionNumberForAddress(start); + int rend = GetRegionNumberForAddress(end); + + if (reaches_limit) { + end += 1; + } + + if ((rend - rstart) == 0) { + return; + } + + uint32_t bitmask = 0; + + if ((OffsetFrom(start) & kRegionAlignmentMask) == 0 + || (start == ObjectAreaStart())) { + // First region is fully covered + bitmask = 1 << rstart; + } + + while (++rstart < rend) { + bitmask |= 1 << rstart; + } + + if (bitmask) { + SetRegionMarks(GetRegionMarks() & ~bitmask); + } +} + + +void Page::FlipMeaningOfInvalidatedWatermarkFlag(Heap* heap) { + heap->page_watermark_invalidated_mark_ ^= 1 << WATERMARK_INVALIDATED; +} + + +bool Page::IsWatermarkValid() { + return (flags_ & (1 << WATERMARK_INVALIDATED)) != + heap_->page_watermark_invalidated_mark_; +} + + +void Page::InvalidateWatermark(bool value) { + if (value) { + flags_ = (flags_ & ~(1 << WATERMARK_INVALIDATED)) | + heap_->page_watermark_invalidated_mark_; + } else { + flags_ = + (flags_ & ~(1 << WATERMARK_INVALIDATED)) | + (heap_->page_watermark_invalidated_mark_ ^ + (1 << WATERMARK_INVALIDATED)); + } + + ASSERT(IsWatermarkValid() == !value); +} + + +bool Page::GetPageFlag(PageFlag flag) { + return (flags_ & static_cast<intptr_t>(1 << flag)) != 0; +} + + +void Page::SetPageFlag(PageFlag flag, bool value) { + if (value) { + flags_ |= static_cast<intptr_t>(1 << flag); + } else { + flags_ &= ~static_cast<intptr_t>(1 << flag); + } +} + + +void Page::ClearPageFlags() { + flags_ = 0; +} + + +void Page::ClearGCFields() { + InvalidateWatermark(true); + SetAllocationWatermark(ObjectAreaStart()); + if (heap_->gc_state() == Heap::SCAVENGE) { + SetCachedAllocationWatermark(ObjectAreaStart()); + } + SetRegionMarks(kAllRegionsCleanMarks); +} + + +bool Page::WasInUseBeforeMC() { + return GetPageFlag(WAS_IN_USE_BEFORE_MC); +} + + +void Page::SetWasInUseBeforeMC(bool was_in_use) { + SetPageFlag(WAS_IN_USE_BEFORE_MC, was_in_use); +} + + +bool Page::IsLargeObjectPage() { + return !GetPageFlag(IS_NORMAL_PAGE); +} + + +void Page::SetIsLargeObjectPage(bool is_large_object_page) { + SetPageFlag(IS_NORMAL_PAGE, !is_large_object_page); +} + +bool Page::IsPageExecutable() { + return GetPageFlag(IS_EXECUTABLE); +} + + +void Page::SetIsPageExecutable(bool is_page_executable) { + SetPageFlag(IS_EXECUTABLE, is_page_executable); +} + + +// ----------------------------------------------------------------------------- +// MemoryAllocator + +void MemoryAllocator::ChunkInfo::init(Address a, size_t s, PagedSpace* o) { + address_ = a; + size_ = s; + owner_ = o; + executable_ = (o == NULL) ? NOT_EXECUTABLE : o->executable(); + owner_identity_ = (o == NULL) ? FIRST_SPACE : o->identity(); +} + + +bool MemoryAllocator::IsValidChunk(int chunk_id) { + if (!IsValidChunkId(chunk_id)) return false; + + ChunkInfo& c = chunks_[chunk_id]; + return (c.address() != NULL) && (c.size() != 0) && (c.owner() != NULL); +} + + +bool MemoryAllocator::IsValidChunkId(int chunk_id) { + return (0 <= chunk_id) && (chunk_id < max_nof_chunks_); +} + + +bool MemoryAllocator::IsPageInSpace(Page* p, PagedSpace* space) { + ASSERT(p->is_valid()); + + int chunk_id = GetChunkId(p); + if (!IsValidChunkId(chunk_id)) return false; + + ChunkInfo& c = chunks_[chunk_id]; + return (c.address() <= p->address()) && + (p->address() < c.address() + c.size()) && + (space == c.owner()); +} + + +Page* MemoryAllocator::GetNextPage(Page* p) { + ASSERT(p->is_valid()); + intptr_t raw_addr = p->opaque_header & ~Page::kPageAlignmentMask; + return Page::FromAddress(AddressFrom<Address>(raw_addr)); +} + + +int MemoryAllocator::GetChunkId(Page* p) { + ASSERT(p->is_valid()); + return static_cast<int>(p->opaque_header & Page::kPageAlignmentMask); +} + + +void MemoryAllocator::SetNextPage(Page* prev, Page* next) { + ASSERT(prev->is_valid()); + int chunk_id = GetChunkId(prev); + ASSERT_PAGE_ALIGNED(next->address()); + prev->opaque_header = OffsetFrom(next->address()) | chunk_id; +} + + +PagedSpace* MemoryAllocator::PageOwner(Page* page) { + int chunk_id = GetChunkId(page); + ASSERT(IsValidChunk(chunk_id)); + return chunks_[chunk_id].owner(); +} + + +bool MemoryAllocator::InInitialChunk(Address address) { + if (initial_chunk_ == NULL) return false; + + Address start = static_cast<Address>(initial_chunk_->address()); + return (start <= address) && (address < start + initial_chunk_->size()); +} + + +#ifdef ENABLE_HEAP_PROTECTION + +void MemoryAllocator::Protect(Address start, size_t size) { + OS::Protect(start, size); +} + + +void MemoryAllocator::Unprotect(Address start, + size_t size, + Executability executable) { + OS::Unprotect(start, size, executable); +} + + +void MemoryAllocator::ProtectChunkFromPage(Page* page) { + int id = GetChunkId(page); + OS::Protect(chunks_[id].address(), chunks_[id].size()); +} + + +void MemoryAllocator::UnprotectChunkFromPage(Page* page) { + int id = GetChunkId(page); + OS::Unprotect(chunks_[id].address(), chunks_[id].size(), + chunks_[id].owner()->executable() == EXECUTABLE); +} + +#endif + + +// -------------------------------------------------------------------------- +// PagedSpace + +bool PagedSpace::Contains(Address addr) { + Page* p = Page::FromAddress(addr); + if (!p->is_valid()) return false; + return heap()->isolate()->memory_allocator()->IsPageInSpace(p, this); +} + + +// Try linear allocation in the page of alloc_info's allocation top. Does +// not contain slow case logic (eg, move to the next page or try free list +// allocation) so it can be used by all the allocation functions and for all +// the paged spaces. +HeapObject* PagedSpace::AllocateLinearly(AllocationInfo* alloc_info, + int size_in_bytes) { + Address current_top = alloc_info->top; + Address new_top = current_top + size_in_bytes; + if (new_top > alloc_info->limit) return NULL; + + alloc_info->top = new_top; + ASSERT(alloc_info->VerifyPagedAllocation()); + accounting_stats_.AllocateBytes(size_in_bytes); + return HeapObject::FromAddress(current_top); +} + + +// Raw allocation. +MaybeObject* PagedSpace::AllocateRaw(int size_in_bytes) { + ASSERT(HasBeenSetup()); + ASSERT_OBJECT_SIZE(size_in_bytes); + HeapObject* object = AllocateLinearly(&allocation_info_, size_in_bytes); + if (object != NULL) return object; + + object = SlowAllocateRaw(size_in_bytes); + if (object != NULL) return object; + + return Failure::RetryAfterGC(identity()); +} + + +// Reallocating (and promoting) objects during a compacting collection. +MaybeObject* PagedSpace::MCAllocateRaw(int size_in_bytes) { + ASSERT(HasBeenSetup()); + ASSERT_OBJECT_SIZE(size_in_bytes); + HeapObject* object = AllocateLinearly(&mc_forwarding_info_, size_in_bytes); + if (object != NULL) return object; + + object = SlowMCAllocateRaw(size_in_bytes); + if (object != NULL) return object; + + return Failure::RetryAfterGC(identity()); +} + + +// ----------------------------------------------------------------------------- +// LargeObjectChunk + +Address LargeObjectChunk::GetStartAddress() { + // Round the chunk address up to the nearest page-aligned address + // and return the heap object in that page. + Page* page = Page::FromAddress(RoundUp(address(), Page::kPageSize)); + return page->ObjectAreaStart(); +} + + +void LargeObjectChunk::Free(Executability executable) { + Isolate* isolate = + Page::FromAddress(RoundUp(address(), Page::kPageSize))->heap_->isolate(); + isolate->memory_allocator()->FreeRawMemory(address(), size(), executable); +} + +// ----------------------------------------------------------------------------- +// NewSpace + +MaybeObject* NewSpace::AllocateRawInternal(int size_in_bytes, + AllocationInfo* alloc_info) { + Address new_top = alloc_info->top + size_in_bytes; + if (new_top > alloc_info->limit) return Failure::RetryAfterGC(); + + Object* obj = HeapObject::FromAddress(alloc_info->top); + alloc_info->top = new_top; +#ifdef DEBUG + SemiSpace* space = + (alloc_info == &allocation_info_) ? &to_space_ : &from_space_; + ASSERT(space->low() <= alloc_info->top + && alloc_info->top <= space->high() + && alloc_info->limit == space->high()); +#endif + return obj; +} + + +intptr_t LargeObjectSpace::Available() { + return LargeObjectChunk::ObjectSizeFor( + heap()->isolate()->memory_allocator()->Available()); +} + + +template <typename StringType> +void NewSpace::ShrinkStringAtAllocationBoundary(String* string, int length) { + ASSERT(length <= string->length()); + ASSERT(string->IsSeqString()); + ASSERT(string->address() + StringType::SizeFor(string->length()) == + allocation_info_.top); + allocation_info_.top = + string->address() + StringType::SizeFor(length); + string->set_length(length); +} + + +bool FreeListNode::IsFreeListNode(HeapObject* object) { + return object->map() == HEAP->raw_unchecked_byte_array_map() + || object->map() == HEAP->raw_unchecked_one_pointer_filler_map() + || object->map() == HEAP->raw_unchecked_two_pointer_filler_map(); +} + +} } // namespace v8::internal + +#endif // V8_SPACES_INL_H_ |