/* * Copyright (C) 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 JSC_Region_h #define JSC_Region_h #include "HeapBlock.h" #include "SuperRegion.h" #include #include #include #define HEAP_MEMORY_ID reinterpret_cast(static_cast(-3)) #define ENABLE_SUPER_REGION 0 #ifndef ENABLE_SUPER_REGION #if USE(JSVALUE64) #define ENABLE_SUPER_REGION 1 #else #define ENABLE_SUPER_REGION 0 #endif #endif namespace JSC { class DeadBlock : public HeapBlock { public: DeadBlock(Region*); }; inline DeadBlock::DeadBlock(Region* region) : HeapBlock(region) { } class Region : public DoublyLinkedListNode { friend CLASS_IF_GCC DoublyLinkedListNode; friend class BlockAllocator; public: ~Region(); static Region* create(SuperRegion*, size_t blockSize); static Region* createCustomSize(SuperRegion*, size_t blockSize, size_t blockAlignment); Region* reset(size_t blockSize); void destroy(); size_t blockSize() const { return m_blockSize; } bool isFull() const { return m_blocksInUse == m_totalBlocks; } bool isEmpty() const { return !m_blocksInUse; } bool isCustomSize() const { return m_isCustomSize; } DeadBlock* allocate(); void deallocate(void*); static const size_t s_regionSize = 64 * KB; static const size_t s_regionMask = ~(s_regionSize - 1); protected: Region(size_t blockSize, size_t totalBlocks, bool isExcess); void initializeBlockList(); bool m_isExcess; private: void* base(); size_t size(); size_t m_totalBlocks; size_t m_blocksInUse; size_t m_blockSize; bool m_isCustomSize; Region* m_prev; Region* m_next; DoublyLinkedList m_deadBlocks; }; class NormalRegion : public Region { friend class Region; private: NormalRegion(PassRefPtr, size_t blockSize, size_t totalBlocks); static NormalRegion* tryCreate(SuperRegion*, size_t blockSize); static NormalRegion* tryCreateCustomSize(SuperRegion*, size_t blockSize, size_t blockAlignment); void* base() { return m_allocation->start(); } size_t size() { return m_allocation->sizeInBytes(); } NormalRegion* reset(size_t blockSize); RefPtr m_allocation; }; class ExcessRegion : public Region { friend class Region; private: ExcessRegion(PageAllocationAligned&, size_t blockSize, size_t totalBlocks); ~ExcessRegion(); static ExcessRegion* create(size_t blockSize); static ExcessRegion* createCustomSize(size_t blockSize, size_t blockAlignment); void* base() { return m_allocation.base(); } size_t size() { return m_allocation.size(); } ExcessRegion* reset(size_t blockSize); PageAllocationAligned m_allocation; }; inline NormalRegion::NormalRegion(PassRefPtr allocation, size_t blockSize, size_t totalBlocks) : Region(blockSize, totalBlocks, false) , m_allocation(allocation) { initializeBlockList(); } inline NormalRegion* NormalRegion::tryCreate(SuperRegion* superRegion, size_t blockSize) { RefPtr allocation = superRegion->allocate(s_regionSize, HEAP_MEMORY_ID); if (!allocation) return 0; return new NormalRegion(allocation, blockSize, s_regionSize / blockSize); } inline NormalRegion* NormalRegion::tryCreateCustomSize(SuperRegion* superRegion, size_t blockSize, size_t blockAlignment) { ASSERT_UNUSED(blockAlignment, blockAlignment <= s_regionSize); RefPtr allocation = superRegion->allocate(blockSize, HEAP_MEMORY_ID); if (!allocation) return 0; return new NormalRegion(allocation, blockSize, 1); } inline NormalRegion* NormalRegion::reset(size_t blockSize) { ASSERT(!m_isExcess); RefPtr allocation = m_allocation.release(); return new (NotNull, this) NormalRegion(allocation.release(), blockSize, s_regionSize / blockSize); } inline ExcessRegion::ExcessRegion(PageAllocationAligned& allocation, size_t blockSize, size_t totalBlocks) : Region(blockSize, totalBlocks, true) , m_allocation(allocation) { initializeBlockList(); } inline ExcessRegion::~ExcessRegion() { m_allocation.deallocate(); } inline ExcessRegion* ExcessRegion::create(size_t blockSize) { PageAllocationAligned allocation = PageAllocationAligned::allocate(s_regionSize, s_regionSize, OSAllocator::JSGCHeapPages); ASSERT(static_cast(allocation)); return new ExcessRegion(allocation, blockSize, s_regionSize / blockSize); } inline ExcessRegion* ExcessRegion::createCustomSize(size_t blockSize, size_t blockAlignment) { PageAllocationAligned allocation = PageAllocationAligned::allocate(blockSize, blockAlignment, OSAllocator::JSGCHeapPages); ASSERT(static_cast(allocation)); return new ExcessRegion(allocation, blockSize, 1); } inline ExcessRegion* ExcessRegion::reset(size_t blockSize) { ASSERT(m_isExcess); PageAllocationAligned allocation = m_allocation; return new (NotNull, this) ExcessRegion(allocation, blockSize, s_regionSize / blockSize); } inline Region::Region(size_t blockSize, size_t totalBlocks, bool isExcess) : DoublyLinkedListNode() , m_isExcess(isExcess) , m_totalBlocks(totalBlocks) , m_blocksInUse(0) , m_blockSize(blockSize) , m_isCustomSize(false) , m_prev(0) , m_next(0) { } inline void Region::initializeBlockList() { char* start = static_cast(base()); char* current = start; for (size_t i = 0; i < m_totalBlocks; i++) { ASSERT(current < start + size()); m_deadBlocks.append(new (NotNull, current) DeadBlock(this)); current += m_blockSize; } } inline Region* Region::create(SuperRegion* superRegion, size_t blockSize) { #if ENABLE(SUPER_REGION) ASSERT(blockSize <= s_regionSize); ASSERT(!(s_regionSize % blockSize)); Region* region = NormalRegion::tryCreate(superRegion, blockSize); if (LIKELY(!!region)) return region; #else UNUSED_PARAM(superRegion); #endif return ExcessRegion::create(blockSize); } inline Region* Region::createCustomSize(SuperRegion* superRegion, size_t blockSize, size_t blockAlignment) { #if ENABLE(SUPER_REGION) Region* region = NormalRegion::tryCreateCustomSize(superRegion, blockSize, blockAlignment); if (UNLIKELY(!region)) region = ExcessRegion::createCustomSize(blockSize, blockAlignment); #else UNUSED_PARAM(superRegion); Region* region = ExcessRegion::createCustomSize(blockSize, blockAlignment); #endif region->m_isCustomSize = true; return region; } inline Region::~Region() { ASSERT(isEmpty()); } inline void Region::destroy() { #if ENABLE(SUPER_REGION) if (UNLIKELY(m_isExcess)) delete static_cast(this); else delete static_cast(this); #else delete static_cast(this); #endif } inline Region* Region::reset(size_t blockSize) { #if ENABLE(SUPER_REGION) ASSERT(isEmpty()); if (UNLIKELY(m_isExcess)) return static_cast(this)->reset(blockSize); return static_cast(this)->reset(blockSize); #else return static_cast(this)->reset(blockSize); #endif } inline DeadBlock* Region::allocate() { ASSERT(!isFull()); m_blocksInUse++; return m_deadBlocks.removeHead(); } inline void Region::deallocate(void* base) { ASSERT(base); ASSERT(m_blocksInUse); ASSERT(base >= this->base() && base < static_cast(this->base()) + size()); DeadBlock* block = new (NotNull, base) DeadBlock(this); m_deadBlocks.push(block); m_blocksInUse--; } inline void* Region::base() { #if ENABLE(SUPER_REGION) if (UNLIKELY(m_isExcess)) return static_cast(this)->ExcessRegion::base(); return static_cast(this)->NormalRegion::base(); #else return static_cast(this)->ExcessRegion::base(); #endif } inline size_t Region::size() { #if ENABLE(SUPER_REGION) if (UNLIKELY(m_isExcess)) return static_cast(this)->ExcessRegion::size(); return static_cast(this)->NormalRegion::size(); #else return static_cast(this)->ExcessRegion::size(); #endif } } // namespace JSC #endif // JSC_Region_h