diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/JavaScriptCore/heap/Subspace.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/JavaScriptCore/heap/Subspace.cpp')
-rw-r--r-- | Source/JavaScriptCore/heap/Subspace.cpp | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/heap/Subspace.cpp b/Source/JavaScriptCore/heap/Subspace.cpp new file mode 100644 index 000000000..ed6c19eac --- /dev/null +++ b/Source/JavaScriptCore/heap/Subspace.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2017 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. ``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 + * 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. + */ + +#include "config.h" +#include "Subspace.h" + +#include "JSCInlines.h" +#include "MarkedAllocatorInlines.h" +#include "MarkedBlockInlines.h" +#include "PreventCollectionScope.h" +#include "SubspaceInlines.h" + +namespace JSC { + +namespace { + +// Writing it this way ensures that when you pass this as a functor, the callee is specialized for +// this callback. If you wrote this as a normal function then the callee would be specialized for +// the function's type and it would have indirect calls to that function. And unlike a lambda, it's +// possible to mark this ALWAYS_INLINE. +struct DestroyFunc { + ALWAYS_INLINE void operator()(VM& vm, JSCell* cell) const + { + ASSERT(cell->structureID()); + ASSERT(cell->inlineTypeFlags() & StructureIsImmortal); + Structure* structure = cell->structure(vm); + const ClassInfo* classInfo = structure->classInfo(); + MethodTable::DestroyFunctionPtr destroy = classInfo->methodTable.destroy; + destroy(cell); + } +}; + +} // anonymous namespace + +Subspace::Subspace(CString name, Heap& heap, AllocatorAttributes attributes) + : m_space(heap.objectSpace()) + , m_name(name) + , m_attributes(attributes) +{ + // It's remotely possible that we're GCing right now even if the client is careful to only + // create subspaces right after VM creation, since collectContinuously (and probably other + // things) could cause a GC to be launched at pretty much any time and it's not 100% obvious + // that all clients would be able to ensure that there are zero safepoints between when they + // create VM and when they do this. Preventing GC while we're creating the Subspace ensures + // that we don't have to worry about whether it's OK for the GC to ever see a brand new + // subspace. + PreventCollectionScope preventCollectionScope(heap); + heap.objectSpace().m_subspaces.append(this); + + for (size_t i = MarkedSpace::numSizeClasses; i--;) + m_allocatorForSizeStep[i] = nullptr; +} + +Subspace::~Subspace() +{ +} + +FreeList Subspace::finishSweep(MarkedBlock::Handle& block, MarkedBlock::Handle::SweepMode sweepMode) +{ + return block.finishSweepKnowingSubspace(sweepMode, DestroyFunc()); +} + +void Subspace::destroy(VM& vm, JSCell* cell) +{ + DestroyFunc()(vm, cell); +} + +// The reason why we distinguish between allocate and tryAllocate is to minimize the number of +// checks on the allocation path in both cases. Likewise, the reason why we have overloads with and +// without deferralContext is to minimize the amount of code for calling allocate when you don't +// need the deferralContext. +void* Subspace::allocate(size_t size) +{ + if (MarkedAllocator* allocator = tryAllocatorFor(size)) + return allocator->allocate(); + return allocateSlow(nullptr, size); +} + +void* Subspace::allocate(GCDeferralContext* deferralContext, size_t size) +{ + if (MarkedAllocator* allocator = tryAllocatorFor(size)) + return allocator->allocate(deferralContext); + return allocateSlow(deferralContext, size); +} + +void* Subspace::tryAllocate(size_t size) +{ + if (MarkedAllocator* allocator = tryAllocatorFor(size)) + return allocator->tryAllocate(); + return tryAllocateSlow(nullptr, size); +} + +void* Subspace::tryAllocate(GCDeferralContext* deferralContext, size_t size) +{ + if (MarkedAllocator* allocator = tryAllocatorFor(size)) + return allocator->tryAllocate(deferralContext); + return tryAllocateSlow(deferralContext, size); +} + +MarkedAllocator* Subspace::allocatorForSlow(size_t size) +{ + size_t index = MarkedSpace::sizeClassToIndex(size); + size_t sizeClass = MarkedSpace::s_sizeClassForSizeStep[index]; + if (!sizeClass) + return nullptr; + + // This is written in such a way that it's OK for the JIT threads to end up here if they want + // to generate code that uses some allocator that hadn't been used yet. Note that a possibly- + // just-as-good solution would be to return null if we're in the JIT since the JIT treats null + // allocator as "please always take the slow path". But, that could lead to performance + // surprises and the algorithm here is pretty easy. Only this code has to hold the lock, to + // prevent simultaneously MarkedAllocator creations from multiple threads. This code ensures + // that any "forEachAllocator" traversals will only see this allocator after it's initialized + // enough: it will have + auto locker = holdLock(m_space.allocatorLock()); + if (MarkedAllocator* allocator = m_allocatorForSizeStep[index]) + return allocator; + + if (false) + dataLog("Creating marked allocator for ", m_name, ", ", m_attributes, ", ", sizeClass, ".\n"); + MarkedAllocator* allocator = m_space.addMarkedAllocator(locker, this, sizeClass); + index = MarkedSpace::sizeClassToIndex(sizeClass); + for (;;) { + if (MarkedSpace::s_sizeClassForSizeStep[index] != sizeClass) + break; + + m_allocatorForSizeStep[index] = allocator; + + if (!index--) + break; + } + allocator->setNextAllocatorInSubspace(m_firstAllocator); + WTF::storeStoreFence(); + m_firstAllocator = allocator; + return allocator; +} + +void* Subspace::allocateSlow(GCDeferralContext* deferralContext, size_t size) +{ + void* result = tryAllocateSlow(deferralContext, size); + RELEASE_ASSERT(result); + return result; +} + +void* Subspace::tryAllocateSlow(GCDeferralContext* deferralContext, size_t size) +{ + if (MarkedAllocator* allocator = allocatorFor(size)) + return allocator->tryAllocate(deferralContext); + + if (size <= Options::largeAllocationCutoff() + && size <= MarkedSpace::largeCutoff) { + dataLog("FATAL: attampting to allocate small object using large allocation.\n"); + dataLog("Requested allocation size: ", size, "\n"); + RELEASE_ASSERT_NOT_REACHED(); + } + + m_space.heap()->collectIfNecessaryOrDefer(deferralContext); + + size = WTF::roundUpToMultipleOf<MarkedSpace::sizeStep>(size); + LargeAllocation* allocation = LargeAllocation::tryCreate(*m_space.m_heap, size, this); + if (!allocation) + return nullptr; + + m_space.m_largeAllocations.append(allocation); + m_space.m_heap->didAllocate(size); + m_space.m_capacity += size; + + m_largeAllocations.append(allocation); + + return allocation->cell(); +} + +} // namespace JSC + |