Remove excessive headers from JavaScriptCore
[WebKit-https.git] / Source / JavaScriptCore / heap / MarkedAllocator.h
index 6866914..0ea3dca 100644 (file)
-#ifndef MarkedAllocator_h
-#define MarkedAllocator_h
+/*
+ * Copyright (C) 2012-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. 
+ */
 
+#pragma once
+
+#include "AllocatorAttributes.h"
+#include "FreeList.h"
 #include "MarkedBlock.h"
-#include <wtf/DoublyLinkedList.h>
+#include <wtf/DataLog.h>
+#include <wtf/FastBitVector.h>
+#include <wtf/Vector.h>
 
 namespace JSC {
 
+class GCDeferralContext;
 class Heap;
 class MarkedSpace;
 class LLIntOffsetsExtractor;
 
-namespace DFG {
-class SpeculativeJIT;
-}
+#define FOR_EACH_MARKED_ALLOCATOR_BIT(macro) \
+    macro(live, Live) /* The set of block indices that have actual blocks. */\
+    macro(empty, Empty) /* The set of all blocks that have no live objects and nothing to destroy. */ \
+    macro(allocated, Allocated) /* The set of all blocks that are full of live objects. */\
+    macro(canAllocateButNotEmpty, CanAllocateButNotEmpty) /* The set of all blocks are neither empty nor retired (i.e. are more than minMarkedBlockUtilization full). */ \
+    macro(eden, Eden) /* The set of all blocks that have new objects since the last GC. */\
+    macro(unswept, Unswept) /* The set of all blocks that could be swept by the incremental sweeper. */\
+    \
+    /* These are computed during marking. */\
+    macro(markingNotEmpty, MarkingNotEmpty) /* The set of all blocks that are not empty. */ \
+    macro(markingRetired, MarkingRetired) /* The set of all blocks that are retired. */
+
+// FIXME: We defined canAllocateButNotEmpty and empty to be exclusive:
+//
+//     canAllocateButNotEmpty & empty == 0
+//
+// Instead of calling it canAllocate and making it inclusive:
+//
+//     canAllocate & empty == empty
+//
+// The latter is probably better. I'll leave it to a future bug to fix that, since breathing on
+// this code leads to regressions for days, and it's not clear that making this change would
+// improve perf since it would not change the collector's behavior, and either way the allocator
+// has to look at both bitvectors.
+// https://bugs.webkit.org/show_bug.cgi?id=162121
+
+// Note that this collector supports overlapping allocator state with marking state, since in a
+// concurrent collector you allow allocation while marking is running. So it's best to visualize a
+// full mutable->eden collect->mutate->full collect cycle and see how the bits above get affected.
+// The example below tries to be exhaustive about what happens to the bits, but omits a lot of
+// things that happen to other state.
+//
+// Create allocator
+// - all bits are empty
+// Start allocating in some block
+// - allocate the block and set the live bit.
+// - the empty bit for the block flickers on and then gets immediately cleared by sweeping.
+// - set the eden bit.
+// Finish allocating in that block
+// - set the allocated bit.
+// Do that to a lot of blocks and then start an eden collection.
+// - beginMarking() has nothing to do.
+// - by default we have cleared markingNotEmpty/markingRetired bits.
+// - marking builds up markingNotEmpty/markingRetired bits.
+// We do endMarking()
+// - clear all allocated bits.
+// - for destructor blocks: fragmented = live & ~markingRetired
+// - for non-destructor blocks:
+//       empty = live & ~markingNotEmpty
+//       fragmented = live & markingNotEmpty & ~markingRetired
+// Snapshotting.
+// - unswept |= eden
+// Prepare for allocation.
+// - clear eden
+// Finish collection.
+// Allocate in some block that had some free and some live objects.
+// - clear the canAllocateButNotEmpty bit
+// - clear the unswept bit
+// - set the eden bit
+// Finish allocating (set the allocated bit).
+// Allocate in some block that was completely empty.
+// - clear the empty bit
+// - clear the unswept bit
+// - set the eden bit.
+// Finish allocating (set the allocated bit).
+// Allocate in some block that was completely empty in another allocator.
+// - clear the empty bit
+// - clear all bits in that allocator
+// - set the live bit in another allocator and the empty bit.
+// - clear the empty, unswept bits.
+// - set the eden bit.
+// Finish allocating (set the allocated bit).
+// Start a full collection.
+// - beginMarking() clears markingNotEmpty, markingRetired
+// - the heap version is incremented
+// - marking rebuilds markingNotEmpty/markingretired bits.
+// We do endMarking()
+// - clear all allocated bits.
+// - set canAllocateButNotEmpty/empty the same way as in eden collection.
+// Snapshotting.
+// - unswept = live
+// prepare for allocation.
+// - clear eden.
+// Finish collection.
+//
+// Notice how in this scheme, the empty/canAllocateButNotEmpty state stays separate from the
+// markingNotEmpty/markingRetired state. This is one step towards having separated allocation and
+// marking state.
 
 class MarkedAllocator {
     friend class LLIntOffsetsExtractor;
 
 public:
-    static ptrdiff_t offsetOfFreeListHead();
-
-    MarkedAllocator();
-    void reset();
-    void canonicalizeCellLivenessData();
-    size_t cellSize() { return m_cellSize; }
-    MarkedBlock::DestructorType destructorType() { return m_destructorType; }
-    void* allocate(size_t);
+    static ptrdiff_t offsetOfFreeList();
+    static ptrdiff_t offsetOfCellSize();
+
+    MarkedAllocator(Heap*, Subspace*, size_t cellSize);
+    void lastChanceToFinalize();
+    void prepareForAllocation();
+    void stopAllocating();
+    void resumeAllocating();
+    void beginMarkingForFullCollection();
+    void endMarking();
+    void snapshotUnsweptForEdenCollection();
+    void snapshotUnsweptForFullCollection();
+    void sweep();
+    void shrink();
+    void assertNoUnswept();
+    size_t cellSize() const { return m_cellSize; }
+    const AllocatorAttributes& attributes() const { return m_attributes; }
+    bool needsDestruction() const { return m_attributes.destruction == NeedsDestruction; }
+    DestructionMode destruction() const { return m_attributes.destruction; }
+    HeapCell::Kind cellKind() const { return m_attributes.cellKind; }
+    void* allocate(GCDeferralContext* = nullptr);
+    void* tryAllocate(GCDeferralContext* = nullptr);
     Heap* heap() { return m_heap; }
+
+    bool isFreeListedCell(const void* target) const;
+
+    template<typename Functor> void forEachBlock(const Functor&);
+    template<typename Functor> void forEachNotEmptyBlock(const Functor&);
     
-    template<typename Functor> void forEachBlock(Functor&);
-    
-    void addBlock(MarkedBlock*);
-    void removeBlock(MarkedBlock*);
-    void init(Heap*, MarkedSpace*, size_t cellSize, MarkedBlock::DestructorType);
+    void addBlock(MarkedBlock::Handle*);
+    void removeBlock(MarkedBlock::Handle*);
 
     bool isPagedOut(double deadline);
+    
+    static size_t blockSizeForBytes(size_t);
+    
+    Lock& bitvectorLock() { return m_bitvectorLock; }
    
-private:
-    JS_EXPORT_PRIVATE void* allocateSlowCase(size_t);
-    void* tryAllocate(size_t);
-    void* tryAllocateHelper(size_t);
-    MarkedBlock* allocateBlock(size_t);
-    
-    MarkedBlock::FreeList m_freeList;
-    MarkedBlock* m_currentBlock;
-    MarkedBlock* m_blocksToSweep;
-    DoublyLinkedList<MarkedBlock> m_blockList;
-    size_t m_cellSize;
-    MarkedBlock::DestructorType m_destructorType;
-    Heap* m_heap;
-    MarkedSpace* m_markedSpace;
-};
+#define MARKED_ALLOCATOR_BIT_ACCESSORS(lowerBitName, capitalBitName)     \
+    bool is ## capitalBitName(const AbstractLocker&, size_t index) const { return m_ ## lowerBitName[index]; } \
+    bool is ## capitalBitName(const AbstractLocker& locker, MarkedBlock::Handle* block) const { return is ## capitalBitName(locker, block->index()); } \
+    void setIs ## capitalBitName(const AbstractLocker&, size_t index, bool value) { m_ ## lowerBitName[index] = value; } \
+    void setIs ## capitalBitName(const AbstractLocker& locker, MarkedBlock::Handle* block, bool value) { setIs ## capitalBitName(locker, block->index(), value); }
+    FOR_EACH_MARKED_ALLOCATOR_BIT(MARKED_ALLOCATOR_BIT_ACCESSORS)
+#undef MARKED_ALLOCATOR_BIT_ACCESSORS
 
-inline ptrdiff_t MarkedAllocator::offsetOfFreeListHead()
-{
-    return OBJECT_OFFSETOF(MarkedAllocator, m_freeList) + OBJECT_OFFSETOF(MarkedBlock::FreeList, head);
-}
-
-inline MarkedAllocator::MarkedAllocator()
-    : m_currentBlock(0)
-    , m_blocksToSweep(0)
-    , m_cellSize(0)
-    , m_destructorType(MarkedBlock::None)
-    , m_heap(0)
-    , m_markedSpace(0)
-{
-}
-
-inline void MarkedAllocator::init(Heap* heap, MarkedSpace* markedSpace, size_t cellSize, MarkedBlock::DestructorType destructorType)
-{
-    m_heap = heap;
-    m_markedSpace = markedSpace;
-    m_cellSize = cellSize;
-    m_destructorType = destructorType;
-}
-
-inline void* MarkedAllocator::allocate(size_t bytes)
-{
-    MarkedBlock::FreeCell* head = m_freeList.head;
-    if (UNLIKELY(!head)) {
-        void* result = allocateSlowCase(bytes);
-#ifndef NDEBUG
-        memset(result, 0xCD, bytes);
-#endif
-        return result;
+    template<typename Func>
+    void forEachBitVector(const AbstractLocker&, const Func& func)
+    {
+#define MARKED_ALLOCATOR_BIT_CALLBACK(lowerBitName, capitalBitName) \
+        func(m_ ## lowerBitName);
+        FOR_EACH_MARKED_ALLOCATOR_BIT(MARKED_ALLOCATOR_BIT_CALLBACK);
+#undef MARKED_ALLOCATOR_BIT_CALLBACK
     }
     
-    m_freeList.head = head->next;
-#ifndef NDEBUG
-    memset(head, 0xCD, bytes);
-#endif
-    return head;
-}
+    template<typename Func>
+    void forEachBitVectorWithName(const AbstractLocker&, const Func& func)
+    {
+#define MARKED_ALLOCATOR_BIT_CALLBACK(lowerBitName, capitalBitName) \
+        func(m_ ## lowerBitName, #capitalBitName);
+        FOR_EACH_MARKED_ALLOCATOR_BIT(MARKED_ALLOCATOR_BIT_CALLBACK);
+#undef MARKED_ALLOCATOR_BIT_CALLBACK
+    }
+    
+    MarkedAllocator* nextAllocator() const { return m_nextAllocator; }
+    MarkedAllocator* nextAllocatorInSubspace() const { return m_nextAllocatorInSubspace; }
+    
+    void setNextAllocator(MarkedAllocator* allocator) { m_nextAllocator = allocator; }
+    void setNextAllocatorInSubspace(MarkedAllocator* allocator) { m_nextAllocatorInSubspace = allocator; }
+    
+    MarkedBlock::Handle* findEmptyBlockToSteal();
+    
+    MarkedBlock::Handle* findBlockToSweep();
+    
+    Subspace* subspace() const { return m_subspace; }
+    MarkedSpace& markedSpace() const;
+    
+    const FreeList& freeList() const { return m_freeList; }
+    
+    void dump(PrintStream&) const;
+    void dumpBits(PrintStream& = WTF::dataFile());
+    
+private:
+    friend class MarkedBlock;
+    
+    bool shouldStealEmptyBlocksFromOtherAllocators() const;
+    
+    JS_EXPORT_PRIVATE void* allocateSlowCase(GCDeferralContext*);
+    JS_EXPORT_PRIVATE void* tryAllocateSlowCase(GCDeferralContext*);
+    void* allocateSlowCaseImpl(GCDeferralContext*, bool crashOnFailure);
+    void didConsumeFreeList();
+    void* tryAllocateWithoutCollecting();
+    MarkedBlock::Handle* tryAllocateBlock();
+    void* tryAllocateIn(MarkedBlock::Handle*);
+    void* allocateIn(MarkedBlock::Handle*);
+    ALWAYS_INLINE void doTestCollectionsIfNeeded(GCDeferralContext*);
+    
+    FreeList m_freeList;
+    
+    Vector<MarkedBlock::Handle*> m_blocks;
+    Vector<unsigned> m_freeBlockIndices;
 
-inline void MarkedAllocator::reset()
-{
-    m_currentBlock = 0;
-    m_freeList = MarkedBlock::FreeList();
-    m_blocksToSweep = m_blockList.head();
-}
+    // Mutator uses this to guard resizing the bitvectors. Those things in the GC that may run
+    // concurrently to the mutator must lock this when accessing the bitvectors.
+    Lock m_bitvectorLock;
+#define MARKED_ALLOCATOR_BIT_DECLARATION(lowerBitName, capitalBitName) \
+    FastBitVector m_ ## lowerBitName;
+    FOR_EACH_MARKED_ALLOCATOR_BIT(MARKED_ALLOCATOR_BIT_DECLARATION)
+#undef MARKED_ALLOCATOR_BIT_DECLARATION
+    
+    // After you do something to a block based on one of these cursors, you clear the bit in the
+    // corresponding bitvector and leave the cursor where it was.
+    size_t m_allocationCursor { 0 }; // Points to the next block that is a candidate for allocation.
+    size_t m_emptyCursor { 0 }; // Points to the next block that is a candidate for empty allocation (allocating in empty blocks).
+    size_t m_unsweptCursor { 0 }; // Points to the next block that is a candidate for incremental sweeping.
+    
+    MarkedBlock::Handle* m_currentBlock;
+    MarkedBlock::Handle* m_lastActiveBlock;
+
+    Lock m_lock;
+    unsigned m_cellSize;
+    AllocatorAttributes m_attributes;
+    // FIXME: All of these should probably be references.
+    // https://bugs.webkit.org/show_bug.cgi?id=166988
+    Heap* m_heap;
+    Subspace* m_subspace;
+    MarkedAllocator* m_nextAllocator { nullptr };
+    MarkedAllocator* m_nextAllocatorInSubspace { nullptr };
+};
 
-inline void MarkedAllocator::canonicalizeCellLivenessData()
+inline ptrdiff_t MarkedAllocator::offsetOfFreeList()
 {
-    if (!m_currentBlock) {
-        ASSERT(!m_freeList.head);
-        return;
-    }
-    
-    m_currentBlock->canonicalizeCellLivenessData(m_freeList);
-    m_currentBlock = 0;
-    m_freeList = MarkedBlock::FreeList();
+    return OBJECT_OFFSETOF(MarkedAllocator, m_freeList);
 }
 
-template <typename Functor> inline void MarkedAllocator::forEachBlock(Functor& functor)
+inline ptrdiff_t MarkedAllocator::offsetOfCellSize()
 {
-    MarkedBlock* next;
-    for (MarkedBlock* block = m_blockList.head(); block; block = next) {
-        next = block->next();
-        functor(block);
-    }
+    return OBJECT_OFFSETOF(MarkedAllocator, m_cellSize);
 }
-    
-} // namespace JSC
 
-#endif
+} // namespace JSC