Octane/splay can leak memory due to stray pointers on the stack when run from the...
[WebKit-https.git] / Source / JavaScriptCore / heap / Subspace.cpp
1 /*
2  * Copyright (C) 2017 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. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "Subspace.h"
28
29 #include "JSCInlines.h"
30 #include "MarkedAllocatorInlines.h"
31 #include "MarkedBlockInlines.h"
32 #include "PreventCollectionScope.h"
33 #include "SubspaceInlines.h"
34
35 namespace JSC {
36
37 namespace {
38
39 // Writing it this way ensures that when you pass this as a functor, the callee is specialized for
40 // this callback. If you wrote this as a normal function then the callee would be specialized for
41 // the function's type and it would have indirect calls to that function. And unlike a lambda, it's
42 // possible to mark this ALWAYS_INLINE.
43 struct DestroyFunc {
44     ALWAYS_INLINE void operator()(VM& vm, JSCell* cell) const
45     {
46         ASSERT(cell->structureID());
47         ASSERT(cell->inlineTypeFlags() & StructureIsImmortal);
48         Structure* structure = cell->structure(vm);
49         const ClassInfo* classInfo = structure->classInfo();
50         MethodTable::DestroyFunctionPtr destroy = classInfo->methodTable.destroy;
51         destroy(cell);
52     }
53 };
54
55 } // anonymous namespace
56
57 Subspace::Subspace(CString name, Heap& heap, AllocatorAttributes attributes, AlignedMemoryAllocator* alignedMemoryAllocator)
58     : m_space(heap.objectSpace())
59     , m_name(name)
60     , m_attributes(attributes)
61     , m_alignedMemoryAllocator(alignedMemoryAllocator)
62     , m_allocatorForEmptyAllocation(m_space.firstAllocator())
63 {
64     // It's remotely possible that we're GCing right now even if the client is careful to only
65     // create subspaces right after VM creation, since collectContinuously (and probably other
66     // things) could cause a GC to be launched at pretty much any time and it's not 100% obvious
67     // that all clients would be able to ensure that there are zero safepoints between when they
68     // create VM and when they do this. Preventing GC while we're creating the Subspace ensures
69     // that we don't have to worry about whether it's OK for the GC to ever see a brand new
70     // subspace.
71     PreventCollectionScope preventCollectionScope(heap);
72     heap.objectSpace().m_subspaces.append(this);
73     
74     for (size_t i = MarkedSpace::numSizeClasses; i--;)
75         m_allocatorForSizeStep[i] = nullptr;
76 }
77
78 Subspace::~Subspace()
79 {
80 }
81
82 void Subspace::finishSweep(MarkedBlock::Handle& block, FreeList* freeList)
83 {
84     block.finishSweepKnowingSubspace(freeList, DestroyFunc());
85 }
86
87 void Subspace::destroy(VM& vm, JSCell* cell)
88 {
89     DestroyFunc()(vm, cell);
90 }
91
92 // The reason why we distinguish between allocate and tryAllocate is to minimize the number of
93 // checks on the allocation path in both cases. Likewise, the reason why we have overloads with and
94 // without deferralContext is to minimize the amount of code for calling allocate when you don't
95 // need the deferralContext.
96 void* Subspace::allocate(size_t size)
97 {
98     void* result;
99     if (MarkedAllocator* allocator = tryAllocatorFor(size))
100         result = allocator->allocate();
101     else
102         result = allocateSlow(nullptr, size);
103     didAllocate(result);
104     return result;
105 }
106
107 void* Subspace::allocate(GCDeferralContext* deferralContext, size_t size)
108 {
109     void *result;
110     if (MarkedAllocator* allocator = tryAllocatorFor(size))
111         result = allocator->allocate(deferralContext);
112     else
113         result = allocateSlow(deferralContext, size);
114     didAllocate(result);
115     return result;
116 }
117
118 void* Subspace::tryAllocate(size_t size)
119 {
120     void* result;
121     if (MarkedAllocator* allocator = tryAllocatorFor(size))
122         result = allocator->tryAllocate();
123     else
124         result = tryAllocateSlow(nullptr, size);
125     didAllocate(result);
126     return result;
127 }
128
129 void* Subspace::tryAllocate(GCDeferralContext* deferralContext, size_t size)
130 {
131     void* result;
132     if (MarkedAllocator* allocator = tryAllocatorFor(size))
133         result = allocator->tryAllocate(deferralContext);
134     else
135         result = tryAllocateSlow(deferralContext, size);
136     didAllocate(result);
137     return result;
138 }
139
140 void Subspace::prepareForAllocation()
141 {
142     forEachAllocator(
143         [&] (MarkedAllocator& allocator) {
144             allocator.prepareForAllocation();
145         });
146
147     m_allocatorForEmptyAllocation = m_space.firstAllocator();
148 }
149
150 MarkedBlock::Handle* Subspace::findEmptyBlockToSteal()
151 {
152     for (; m_allocatorForEmptyAllocation; m_allocatorForEmptyAllocation = m_allocatorForEmptyAllocation->nextAllocator()) {
153         Subspace* otherSubspace = m_allocatorForEmptyAllocation->subspace();
154         if (otherSubspace->alignedMemoryAllocator() != alignedMemoryAllocator())
155             continue;
156         
157         if (MarkedBlock::Handle* block = m_allocatorForEmptyAllocation->findEmptyBlockToSteal())
158             return block;
159     }
160     return nullptr;
161 }
162
163 MarkedAllocator* Subspace::allocatorForSlow(size_t size)
164 {
165     size_t index = MarkedSpace::sizeClassToIndex(size);
166     size_t sizeClass = MarkedSpace::s_sizeClassForSizeStep[index];
167     if (!sizeClass)
168         return nullptr;
169     
170     // This is written in such a way that it's OK for the JIT threads to end up here if they want
171     // to generate code that uses some allocator that hadn't been used yet. Note that a possibly-
172     // just-as-good solution would be to return null if we're in the JIT since the JIT treats null
173     // allocator as "please always take the slow path". But, that could lead to performance
174     // surprises and the algorithm here is pretty easy. Only this code has to hold the lock, to
175     // prevent simultaneously MarkedAllocator creations from multiple threads. This code ensures
176     // that any "forEachAllocator" traversals will only see this allocator after it's initialized
177     // enough: it will have 
178     auto locker = holdLock(m_space.allocatorLock());
179     if (MarkedAllocator* allocator = m_allocatorForSizeStep[index])
180         return allocator;
181
182     if (false)
183         dataLog("Creating marked allocator for ", m_name, ", ", m_attributes, ", ", sizeClass, ".\n");
184     MarkedAllocator* allocator = m_space.addMarkedAllocator(locker, this, sizeClass);
185     index = MarkedSpace::sizeClassToIndex(sizeClass);
186     for (;;) {
187         if (MarkedSpace::s_sizeClassForSizeStep[index] != sizeClass)
188             break;
189
190         m_allocatorForSizeStep[index] = allocator;
191         
192         if (!index--)
193             break;
194     }
195     allocator->setNextAllocatorInSubspace(m_firstAllocator);
196     WTF::storeStoreFence();
197     m_firstAllocator = allocator;
198     return allocator;
199 }
200
201 void* Subspace::allocateSlow(GCDeferralContext* deferralContext, size_t size)
202 {
203     void* result = tryAllocateSlow(deferralContext, size);
204     RELEASE_ASSERT(result);
205     return result;
206 }
207
208 void* Subspace::tryAllocateSlow(GCDeferralContext* deferralContext, size_t size)
209 {
210     sanitizeStackForVM(m_space.heap()->vm());
211     
212     if (MarkedAllocator* allocator = allocatorFor(size))
213         return allocator->tryAllocate(deferralContext);
214     
215     if (size <= Options::largeAllocationCutoff()
216         && size <= MarkedSpace::largeCutoff) {
217         dataLog("FATAL: attampting to allocate small object using large allocation.\n");
218         dataLog("Requested allocation size: ", size, "\n");
219         RELEASE_ASSERT_NOT_REACHED();
220     }
221     
222     m_space.heap()->collectIfNecessaryOrDefer(deferralContext);
223     
224     size = WTF::roundUpToMultipleOf<MarkedSpace::sizeStep>(size);
225     LargeAllocation* allocation = LargeAllocation::tryCreate(*m_space.m_heap, size, this);
226     if (!allocation)
227         return nullptr;
228     
229     m_space.m_largeAllocations.append(allocation);
230     m_space.m_heap->didAllocate(size);
231     m_space.m_capacity += size;
232     
233     m_largeAllocations.append(allocation);
234         
235     return allocation->cell();
236 }
237
238 ALWAYS_INLINE void Subspace::didAllocate(void* ptr)
239 {
240     UNUSED_PARAM(ptr);
241     
242     // This is useful for logging allocations, or doing other kinds of debugging hacks. Just make
243     // sure you JSC_forceGCSlowPaths=true.
244 }
245
246 } // namespace JSC
247