1/*
2 * Copyright (C) 2013 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#ifndef JSC_Region_h
27#define JSC_Region_h
28
29#include "HeapBlock.h"
30#include "SuperRegion.h"
31#include <wtf/DoublyLinkedList.h>
32#include <wtf/MetaAllocatorHandle.h>
33#include <wtf/PageAllocationAligned.h>
34
35#define HEAP_MEMORY_ID reinterpret_cast<void*>(static_cast<intptr_t>(-3))
36
37#define ENABLE_SUPER_REGION 0
38
39#ifndef ENABLE_SUPER_REGION
40#if USE(JSVALUE64) && !CPU(ARM64)
41#define ENABLE_SUPER_REGION 1
42#else
43#define ENABLE_SUPER_REGION 0
44#endif
45#endif
46
47namespace JSC {
48
49class DeadBlock : public HeapBlock<DeadBlock> {
50public:
51    DeadBlock(Region*);
52};
53
54inline DeadBlock::DeadBlock(Region* region)
55    : HeapBlock<DeadBlock>(region)
56{
57}
58
59class Region : public DoublyLinkedListNode<Region> {
60    WTF_MAKE_FAST_ALLOCATED;
61
62    friend class WTF::DoublyLinkedListNode<Region>;
63    friend class BlockAllocator;
64public:
65    ~Region();
66    static Region* create(SuperRegion*, size_t blockSize);
67    static Region* createCustomSize(SuperRegion*, size_t blockSize, size_t blockAlignment);
68    Region* reset(size_t blockSize);
69    void destroy();
70
71    size_t blockSize() const { return m_blockSize; }
72    bool isFull() const { return m_blocksInUse == m_totalBlocks; }
73    bool isEmpty() const { return !m_blocksInUse; }
74    bool isCustomSize() const { return m_isCustomSize; }
75
76    DeadBlock* allocate();
77    void deallocate(void*);
78
79    static const size_t s_regionSize = 64 * KB;
80    static const size_t s_regionMask = ~(s_regionSize - 1);
81
82protected:
83    Region(size_t blockSize, size_t totalBlocks, bool isExcess);
84    void initializeBlockList();
85
86    bool m_isExcess;
87
88private:
89    void* base();
90    size_t size();
91
92    size_t m_totalBlocks;
93    size_t m_blocksInUse;
94    size_t m_blockSize;
95    bool m_isCustomSize;
96    Region* m_prev;
97    Region* m_next;
98    DoublyLinkedList<DeadBlock> m_deadBlocks;
99};
100
101
102class NormalRegion : public Region {
103    friend class Region;
104private:
105    NormalRegion(PassRefPtr<WTF::MetaAllocatorHandle>, size_t blockSize, size_t totalBlocks);
106
107    static NormalRegion* tryCreate(SuperRegion*, size_t blockSize);
108    static NormalRegion* tryCreateCustomSize(SuperRegion*, size_t blockSize, size_t blockAlignment);
109
110    void* base() { return m_allocation->start(); }
111    size_t size() { return m_allocation->sizeInBytes(); }
112
113    NormalRegion* reset(size_t blockSize);
114
115    RefPtr<WTF::MetaAllocatorHandle> m_allocation;
116};
117
118class ExcessRegion : public Region {
119    friend class Region;
120private:
121    ExcessRegion(PageAllocationAligned&, size_t blockSize, size_t totalBlocks);
122
123    ~ExcessRegion();
124
125    static ExcessRegion* create(size_t blockSize);
126    static ExcessRegion* createCustomSize(size_t blockSize, size_t blockAlignment);
127
128    void* base() { return m_allocation.base(); }
129    size_t size() { return m_allocation.size(); }
130
131    ExcessRegion* reset(size_t blockSize);
132
133    PageAllocationAligned m_allocation;
134};
135
136inline NormalRegion::NormalRegion(PassRefPtr<WTF::MetaAllocatorHandle> allocation, size_t blockSize, size_t totalBlocks)
137    : Region(blockSize, totalBlocks, false)
138    , m_allocation(allocation)
139{
140    initializeBlockList();
141}
142
143inline NormalRegion* NormalRegion::tryCreate(SuperRegion* superRegion, size_t blockSize)
144{
145    RefPtr<WTF::MetaAllocatorHandle> allocation = superRegion->allocate(s_regionSize, HEAP_MEMORY_ID);
146    if (!allocation)
147        return 0;
148    return new NormalRegion(allocation, blockSize, s_regionSize / blockSize);
149}
150
151inline NormalRegion* NormalRegion::tryCreateCustomSize(SuperRegion* superRegion, size_t blockSize, size_t blockAlignment)
152{
153    ASSERT_UNUSED(blockAlignment, blockAlignment <= s_regionSize);
154    RefPtr<WTF::MetaAllocatorHandle> allocation = superRegion->allocate(blockSize, HEAP_MEMORY_ID);
155    if (!allocation)
156        return 0;
157    return new NormalRegion(allocation, blockSize, 1);
158}
159
160inline NormalRegion* NormalRegion::reset(size_t blockSize)
161{
162    ASSERT(!m_isExcess);
163    RefPtr<WTF::MetaAllocatorHandle> allocation = m_allocation.release();
164    return new (NotNull, this) NormalRegion(allocation.release(), blockSize, s_regionSize / blockSize);
165}
166
167inline ExcessRegion::ExcessRegion(PageAllocationAligned& allocation, size_t blockSize, size_t totalBlocks)
168    : Region(blockSize, totalBlocks, true)
169    , m_allocation(allocation)
170{
171    initializeBlockList();
172}
173
174inline ExcessRegion::~ExcessRegion()
175{
176    m_allocation.deallocate();
177}
178
179inline ExcessRegion* ExcessRegion::create(size_t blockSize)
180{
181    PageAllocationAligned allocation = PageAllocationAligned::allocate(s_regionSize, s_regionSize, OSAllocator::JSGCHeapPages);
182    ASSERT(static_cast<bool>(allocation));
183    return new ExcessRegion(allocation, blockSize, s_regionSize / blockSize);
184}
185
186inline ExcessRegion* ExcessRegion::createCustomSize(size_t blockSize, size_t blockAlignment)
187{
188    PageAllocationAligned allocation = PageAllocationAligned::allocate(blockSize, blockAlignment, OSAllocator::JSGCHeapPages);
189    ASSERT(static_cast<bool>(allocation));
190    return new ExcessRegion(allocation, blockSize, 1);
191}
192
193inline ExcessRegion* ExcessRegion::reset(size_t blockSize)
194{
195    ASSERT(m_isExcess);
196    PageAllocationAligned allocation = m_allocation;
197    return new (NotNull, this) ExcessRegion(allocation, blockSize, s_regionSize / blockSize);
198}
199
200inline Region::Region(size_t blockSize, size_t totalBlocks, bool isExcess)
201    : DoublyLinkedListNode<Region>()
202    , m_isExcess(isExcess)
203    , m_totalBlocks(totalBlocks)
204    , m_blocksInUse(0)
205    , m_blockSize(blockSize)
206    , m_isCustomSize(false)
207    , m_prev(0)
208    , m_next(0)
209{
210}
211
212inline void Region::initializeBlockList()
213{
214    char* start = static_cast<char*>(base());
215    char* current = start;
216    for (size_t i = 0; i < m_totalBlocks; i++) {
217        ASSERT(current < start + size());
218        m_deadBlocks.append(new (NotNull, current) DeadBlock(this));
219        current += m_blockSize;
220    }
221}
222
223inline Region* Region::create(SuperRegion* superRegion, size_t blockSize)
224{
225#if ENABLE(SUPER_REGION)
226    ASSERT(blockSize <= s_regionSize);
227    ASSERT(!(s_regionSize % blockSize));
228    Region* region = NormalRegion::tryCreate(superRegion, blockSize);
229    if (LIKELY(!!region))
230        return region;
231#else
232    UNUSED_PARAM(superRegion);
233#endif
234    return ExcessRegion::create(blockSize);
235}
236
237inline Region* Region::createCustomSize(SuperRegion* superRegion, size_t blockSize, size_t blockAlignment)
238{
239#if ENABLE(SUPER_REGION)
240    Region* region = NormalRegion::tryCreateCustomSize(superRegion, blockSize, blockAlignment);
241    if (UNLIKELY(!region))
242        region = ExcessRegion::createCustomSize(blockSize, blockAlignment);
243#else
244    UNUSED_PARAM(superRegion);
245    Region* region = ExcessRegion::createCustomSize(blockSize, blockAlignment);
246#endif
247    region->m_isCustomSize = true;
248    return region;
249}
250
251inline Region::~Region()
252{
253    ASSERT(isEmpty());
254}
255
256inline void Region::destroy()
257{
258#if ENABLE(SUPER_REGION)
259    if (UNLIKELY(m_isExcess))
260        delete static_cast<ExcessRegion*>(this);
261    else
262        delete static_cast<NormalRegion*>(this);
263#else
264    delete static_cast<ExcessRegion*>(this);
265#endif
266}
267
268inline Region* Region::reset(size_t blockSize)
269{
270#if ENABLE(SUPER_REGION)
271    ASSERT(isEmpty());
272    if (UNLIKELY(m_isExcess))
273        return static_cast<ExcessRegion*>(this)->reset(blockSize);
274    return static_cast<NormalRegion*>(this)->reset(blockSize);
275#else
276    return static_cast<ExcessRegion*>(this)->reset(blockSize);
277#endif
278}
279
280inline DeadBlock* Region::allocate()
281{
282    ASSERT(!isFull());
283    m_blocksInUse++;
284    return m_deadBlocks.removeHead();
285}
286
287inline void Region::deallocate(void* base)
288{
289    ASSERT(base);
290    ASSERT(m_blocksInUse);
291    ASSERT(base >= this->base() && base < static_cast<char*>(this->base()) + size());
292    DeadBlock* block = new (NotNull, base) DeadBlock(this);
293    m_deadBlocks.push(block);
294    m_blocksInUse--;
295}
296
297inline void* Region::base()
298{
299#if ENABLE(SUPER_REGION)
300    if (UNLIKELY(m_isExcess))
301        return static_cast<ExcessRegion*>(this)->ExcessRegion::base();
302    return static_cast<NormalRegion*>(this)->NormalRegion::base();
303#else
304    return static_cast<ExcessRegion*>(this)->ExcessRegion::base();
305#endif
306}
307
308inline size_t Region::size()
309{
310#if ENABLE(SUPER_REGION)
311    if (UNLIKELY(m_isExcess))
312        return static_cast<ExcessRegion*>(this)->ExcessRegion::size();
313    return static_cast<NormalRegion*>(this)->NormalRegion::size();
314#else
315    return static_cast<ExcessRegion*>(this)->ExcessRegion::size();
316#endif
317}
318
319} // namespace JSC
320
321#endif // JSC_Region_h
322