Remove Heap::setMarked()
[WebKit-https.git] / Source / JavaScriptCore / heap / MarkedBlock.h
1 /*
2  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3  *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
4  *  Copyright (C) 2003-2009, 2011, 2016 Apple Inc. All rights reserved.
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Lesser General Public
8  *  License as published by the Free Software Foundation; either
9  *  version 2 of the License, or (at your option) any later version.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public
17  *  License along with this library; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifndef MarkedBlock_h
23 #define MarkedBlock_h
24
25 #include "AllocatorAttributes.h"
26 #include "DestructionMode.h"
27 #include "FreeList.h"
28 #include "HeapCell.h"
29 #include "HeapOperation.h"
30 #include "IterationStatus.h"
31 #include "WeakSet.h"
32 #include <wtf/Bitmap.h>
33 #include <wtf/DataLog.h>
34 #include <wtf/DoublyLinkedList.h>
35 #include <wtf/HashFunctions.h>
36 #include <wtf/StdLibExtras.h>
37
38 namespace JSC {
39     
40 class Heap;
41 class JSCell;
42 class MarkedAllocator;
43
44 typedef uintptr_t Bits;
45 typedef uint32_t HeapVersion;
46
47 // Set to log state transitions of blocks.
48 #define HEAP_LOG_BLOCK_STATE_TRANSITIONS 0
49
50 #if HEAP_LOG_BLOCK_STATE_TRANSITIONS
51 #define HEAP_LOG_BLOCK_STATE_TRANSITION(handle) do {            \
52         dataLogF(                                               \
53             "%s:%d %s: block %s = %p, %d\n",                    \
54             __FILE__, __LINE__, __FUNCTION__,                   \
55             #handle, &(handle)->block(), (handle)->m_state);    \
56     } while (false)
57 #else
58 #define HEAP_LOG_BLOCK_STATE_TRANSITION(handle) ((void)0)
59 #endif
60
61 // A marked block is a page-aligned container for heap-allocated objects.
62 // Objects are allocated within cells of the marked block. For a given
63 // marked block, all cells have the same size. Objects smaller than the
64 // cell size may be allocated in the marked block, in which case the
65 // allocation suffers from internal fragmentation: wasted space whose
66 // size is equal to the difference between the cell size and the object
67 // size.
68
69 class MarkedBlock {
70     WTF_MAKE_NONCOPYABLE(MarkedBlock);
71     friend class LLIntOffsetsExtractor;
72     friend struct VerifyMarked;
73
74 public:
75     class Handle;
76 private:
77     friend class Handle;
78 public:
79     enum BlockState : uint8_t { New, FreeListed, Allocated, Marked };
80         
81     static const size_t atomSize = 16; // bytes
82     static const size_t blockSize = 16 * KB;
83     static const size_t blockMask = ~(blockSize - 1); // blockSize must be a power of two.
84
85     static const size_t atomsPerBlock = blockSize / atomSize;
86
87     static_assert(!(MarkedBlock::atomSize & (MarkedBlock::atomSize - 1)), "MarkedBlock::atomSize must be a power of two.");
88     static_assert(!(MarkedBlock::blockSize & (MarkedBlock::blockSize - 1)), "MarkedBlock::blockSize must be a power of two.");
89
90     struct VoidFunctor {
91         typedef void ReturnType;
92         void returnValue() { }
93     };
94
95     class CountFunctor {
96     public:
97         typedef size_t ReturnType;
98
99         CountFunctor() : m_count(0) { }
100         void count(size_t count) const { m_count += count; }
101         ReturnType returnValue() const { return m_count; }
102
103     private:
104         // FIXME: This is mutable because we're using a functor rather than C++ lambdas.
105         // https://bugs.webkit.org/show_bug.cgi?id=159644
106         mutable ReturnType m_count;
107     };
108         
109     class Handle : public BasicRawSentinelNode<Handle> {
110         WTF_MAKE_NONCOPYABLE(Handle);
111         WTF_MAKE_FAST_ALLOCATED;
112         friend class DoublyLinkedListNode<Handle>;
113         friend class LLIntOffsetsExtractor;
114         friend class MarkedBlock;
115         friend struct VerifyMarked;
116     public:
117             
118         ~Handle();
119             
120         MarkedBlock& block();
121             
122         void* cellAlign(void*);
123             
124         bool isEmpty();
125
126         void lastChanceToFinalize();
127
128         MarkedAllocator* allocator() const;
129         Heap* heap() const;
130         VM* vm() const;
131         WeakSet& weakSet();
132             
133         enum SweepMode { SweepOnly, SweepToFreeList };
134         FreeList sweep(SweepMode = SweepOnly);
135         
136         void unsweepWithNoNewlyAllocated();
137         
138         void zap(const FreeList&);
139         
140         void shrink();
141             
142         unsigned visitWeakSet(HeapRootVisitor&);
143         void reapWeakSet();
144             
145         // While allocating from a free list, MarkedBlock temporarily has bogus
146         // cell liveness data. To restore accurate cell liveness data, call one
147         // of these functions:
148         void didConsumeFreeList(); // Call this once you've allocated all the items in the free list.
149         void stopAllocating(const FreeList&);
150         FreeList resumeAllocating(); // Call this if you canonicalized a block for some non-collection related purpose.
151             
152         // Returns true if the "newly allocated" bitmap was non-null 
153         // and was successfully cleared and false otherwise.
154         bool clearNewlyAllocated();
155             
156         void flipForEdenCollection();
157             
158         size_t cellSize();
159         const AllocatorAttributes& attributes() const;
160         DestructionMode destruction() const;
161         bool needsDestruction() const;
162         HeapCell::Kind cellKind() const;
163             
164         size_t markCount();
165         size_t size();
166             
167         bool isLive(const HeapCell*);
168         bool isLiveCell(const void*);
169         bool isMarkedOrNewlyAllocated(const HeapCell*);
170             
171         bool isNewlyAllocated(const void*);
172         void setNewlyAllocated(const void*);
173         void clearNewlyAllocated(const void*);
174         
175         bool hasAnyNewlyAllocated() const { return !!m_newlyAllocated; }
176             
177         bool isAllocated() const;
178         bool isMarked() const;
179         bool isFreeListed() const;
180         bool needsSweeping() const;
181         void willRemoveBlock();
182
183         template <typename Functor> IterationStatus forEachCell(const Functor&);
184         template <typename Functor> IterationStatus forEachLiveCell(const Functor&);
185         template <typename Functor> IterationStatus forEachDeadCell(const Functor&);
186             
187         bool needsFlip();
188             
189         void flipIfNecessaryDuringMarking(HeapVersion);
190         void flipIfNecessary(HeapVersion);
191         void flipIfNecessary();
192             
193         void assertFlipped();
194             
195         bool isOnBlocksToSweep() const { return m_isOnBlocksToSweep; }
196         void setIsOnBlocksToSweep(bool value) { m_isOnBlocksToSweep = value; }
197         
198         BlockState state() const { return m_state; }
199             
200     private:
201         Handle(Heap&, MarkedAllocator*, size_t cellSize, const AllocatorAttributes&, void*);
202             
203         template<DestructionMode>
204         FreeList sweepHelperSelectScribbleMode(SweepMode = SweepOnly);
205             
206         enum ScribbleMode { DontScribble, Scribble };
207             
208         template<DestructionMode, ScribbleMode>
209         FreeList sweepHelperSelectStateAndSweepMode(SweepMode = SweepOnly);
210             
211         enum NewlyAllocatedMode { HasNewlyAllocated, DoesNotHaveNewlyAllocated };
212             
213         template<BlockState, SweepMode, DestructionMode, ScribbleMode, NewlyAllocatedMode>
214         FreeList specializedSweep();
215             
216         template<typename Func>
217         void forEachFreeCell(const FreeList&, const Func&);
218             
219         MarkedBlock::Handle* m_prev;
220         MarkedBlock::Handle* m_next;
221             
222         size_t m_atomsPerCell;
223         size_t m_endAtom; // This is a fuzzy end. Always test for < m_endAtom.
224             
225         std::unique_ptr<WTF::Bitmap<atomsPerBlock>> m_newlyAllocated;
226             
227         AllocatorAttributes m_attributes;
228         BlockState m_state;
229         bool m_isOnBlocksToSweep { false };
230             
231         MarkedAllocator* m_allocator;
232         WeakSet m_weakSet;
233             
234         MarkedBlock* m_block;
235     };
236         
237     static MarkedBlock::Handle* tryCreate(Heap&, MarkedAllocator*, size_t cellSize, const AllocatorAttributes&);
238         
239     Handle& handle();
240         
241     VM* vm() const;
242
243     static bool isAtomAligned(const void*);
244     static MarkedBlock* blockFor(const void*);
245     static size_t firstAtom();
246     size_t atomNumber(const void*);
247         
248     size_t markCount();
249
250     bool isMarked(const void*);
251     bool testAndSetMarked(const void*);
252         
253     bool isMarkedOrNewlyAllocated(const HeapCell*);
254     bool isMarkedOrNewlyAllocatedDuringWeakVisiting(HeapVersion, const HeapCell*);
255
256     bool isAtom(const void*);
257     void clearMarked(const void*);
258         
259     size_t cellSize();
260     const AllocatorAttributes& attributes() const;
261
262     bool hasAnyMarked() const;
263     void noteMarked();
264         
265     WeakSet& weakSet();
266
267     bool needsFlip(HeapVersion);
268     bool needsFlip();
269         
270     void flipIfNecessaryDuringMarking(HeapVersion);
271     void flipIfNecessary(HeapVersion);
272     void flipIfNecessary();
273         
274     void assertFlipped();
275         
276     bool needsDestruction() const { return m_needsDestruction; }
277         
278 private:
279     static const size_t atomAlignmentMask = atomSize - 1;
280
281     typedef char Atom[atomSize];
282
283     MarkedBlock(VM&, Handle&);
284     Atom* atoms();
285         
286     void flipIfNecessaryDuringMarkingSlow();
287     void flipIfNecessarySlow();
288     void clearMarks();
289     void clearHasAnyMarked();
290     
291     void noteMarkedSlow();
292         
293     WTF::Bitmap<atomsPerBlock, WTF::BitmapAtomic, uint8_t> m_marks;
294
295     bool m_needsDestruction;
296     Lock m_lock;
297     
298     // The actual mark count can be computed by doing: m_biasedMarkCount - m_markCountBias. Note
299     // that this count is racy. It will accurately detect whether or not exactly zero things were
300     // marked, but if N things got marked, then this may report anything in the range [1, N] (or
301     // before unbiased, it would be [1 + m_markCountBias, N + m_markCountBias].)
302     int16_t m_biasedMarkCount;
303     
304     // We bias the mark count so that if m_biasedMarkCount >= 0 then the block should be retired.
305     // We go to all this trouble to make marking a bit faster: this way, marking knows when to
306     // retire a block using a js/jns on m_biasedMarkCount.
307     //
308     // For example, if a block has room for 100 objects and retirement happens whenever 90% are
309     // live, then m_markCountBias will be -90. This way, when marking begins, this will cause us to
310     // set m_biasedMarkCount to -90 as well, since:
311     //
312     //     m_biasedMarkCount = actualMarkCount + m_markCountBias.
313     //
314     // Marking an object will increment m_biasedMarkCount. Once 90 objects get marked, we will have
315     // m_biasedMarkCount = 0, which will trigger retirement. In other words, we want to set
316     // m_markCountBias like so:
317     //
318     //     m_markCountBias = -(minMarkedBlockUtilization * cellsPerBlock)
319     //
320     // All of this also means that you can detect if any objects are marked by doing:
321     //
322     //     m_biasedMarkCount != m_markCountBias
323     int16_t m_markCountBias;
324
325     HeapVersion m_version;
326     
327     Handle& m_handle;
328     VM* m_vm;
329 };
330
331 inline MarkedBlock::Handle& MarkedBlock::handle()
332 {
333     return m_handle;
334 }
335
336 inline MarkedBlock& MarkedBlock::Handle::block()
337 {
338     return *m_block;
339 }
340
341 inline size_t MarkedBlock::firstAtom()
342 {
343     return WTF::roundUpToMultipleOf<atomSize>(sizeof(MarkedBlock)) / atomSize;
344 }
345
346 inline MarkedBlock::Atom* MarkedBlock::atoms()
347 {
348     return reinterpret_cast<Atom*>(this);
349 }
350
351 inline bool MarkedBlock::isAtomAligned(const void* p)
352 {
353     return !(reinterpret_cast<Bits>(p) & atomAlignmentMask);
354 }
355
356 inline void* MarkedBlock::Handle::cellAlign(void* p)
357 {
358     Bits base = reinterpret_cast<Bits>(block().atoms() + firstAtom());
359     Bits bits = reinterpret_cast<Bits>(p);
360     bits -= base;
361     bits -= bits % cellSize();
362     bits += base;
363     return reinterpret_cast<void*>(bits);
364 }
365
366 inline MarkedBlock* MarkedBlock::blockFor(const void* p)
367 {
368     return reinterpret_cast<MarkedBlock*>(reinterpret_cast<Bits>(p) & blockMask);
369 }
370
371 inline MarkedAllocator* MarkedBlock::Handle::allocator() const
372 {
373     return m_allocator;
374 }
375
376 inline Heap* MarkedBlock::Handle::heap() const
377 {
378     return m_weakSet.heap();
379 }
380
381 inline VM* MarkedBlock::Handle::vm() const
382 {
383     return m_weakSet.vm();
384 }
385
386 inline VM* MarkedBlock::vm() const
387 {
388     return m_vm;
389 }
390
391 inline WeakSet& MarkedBlock::Handle::weakSet()
392 {
393     return m_weakSet;
394 }
395
396 inline WeakSet& MarkedBlock::weakSet()
397 {
398     return m_handle.weakSet();
399 }
400
401 inline void MarkedBlock::Handle::shrink()
402 {
403     m_weakSet.shrink();
404 }
405
406 inline unsigned MarkedBlock::Handle::visitWeakSet(HeapRootVisitor& heapRootVisitor)
407 {
408     return m_weakSet.visit(heapRootVisitor);
409 }
410
411 inline void MarkedBlock::Handle::reapWeakSet()
412 {
413     m_weakSet.reap();
414 }
415
416 inline size_t MarkedBlock::Handle::cellSize()
417 {
418     return m_atomsPerCell * atomSize;
419 }
420
421 inline size_t MarkedBlock::cellSize()
422 {
423     return m_handle.cellSize();
424 }
425
426 inline const AllocatorAttributes& MarkedBlock::Handle::attributes() const
427 {
428     return m_attributes;
429 }
430
431 inline const AllocatorAttributes& MarkedBlock::attributes() const
432 {
433     return m_handle.attributes();
434 }
435
436 inline bool MarkedBlock::Handle::needsDestruction() const
437 {
438     return m_attributes.destruction == NeedsDestruction;
439 }
440
441 inline DestructionMode MarkedBlock::Handle::destruction() const
442 {
443     return m_attributes.destruction;
444 }
445
446 inline HeapCell::Kind MarkedBlock::Handle::cellKind() const
447 {
448     return m_attributes.cellKind;
449 }
450
451 inline size_t MarkedBlock::Handle::markCount()
452 {
453     return m_block->markCount();
454 }
455
456 inline size_t MarkedBlock::Handle::size()
457 {
458     return markCount() * cellSize();
459 }
460
461 inline size_t MarkedBlock::atomNumber(const void* p)
462 {
463     return (reinterpret_cast<Bits>(p) - reinterpret_cast<Bits>(this)) / atomSize;
464 }
465
466 inline bool MarkedBlock::needsFlip(HeapVersion heapVersion)
467 {
468     return heapVersion != m_version;
469 }
470
471 inline void MarkedBlock::flipIfNecessary(HeapVersion heapVersion)
472 {
473     if (UNLIKELY(needsFlip(heapVersion)))
474         flipIfNecessarySlow();
475 }
476
477 inline void MarkedBlock::flipIfNecessaryDuringMarking(HeapVersion heapVersion)
478 {
479     if (UNLIKELY(needsFlip(heapVersion)))
480         flipIfNecessaryDuringMarkingSlow();
481     WTF::loadLoadFence();
482 }
483
484 inline void MarkedBlock::Handle::flipIfNecessary(HeapVersion heapVersion)
485 {
486     block().flipIfNecessary(heapVersion);
487 }
488
489 inline void MarkedBlock::Handle::flipIfNecessaryDuringMarking(HeapVersion heapVersion)
490 {
491     block().flipIfNecessaryDuringMarking(heapVersion);
492 }
493
494 inline void MarkedBlock::Handle::flipForEdenCollection()
495 {
496     assertFlipped();
497         
498     HEAP_LOG_BLOCK_STATE_TRANSITION(this);
499     
500     ASSERT(m_state != New && m_state != FreeListed);
501     
502     m_state = Marked;
503 }
504
505 #if ASSERT_DISABLED
506 inline void MarkedBlock::assertFlipped()
507 {
508 }
509 #endif // ASSERT_DISABLED
510
511 inline void MarkedBlock::Handle::assertFlipped()
512 {
513     block().assertFlipped();
514 }
515
516 inline bool MarkedBlock::isMarked(const void* p)
517 {
518     assertFlipped();
519     return m_marks.get(atomNumber(p));
520 }
521
522 inline bool MarkedBlock::testAndSetMarked(const void* p)
523 {
524     assertFlipped();
525     return m_marks.concurrentTestAndSet(atomNumber(p));
526 }
527
528 inline bool MarkedBlock::Handle::isNewlyAllocated(const void* p)
529 {
530     return m_newlyAllocated->get(m_block->atomNumber(p));
531 }
532
533 inline void MarkedBlock::Handle::setNewlyAllocated(const void* p)
534 {
535     m_newlyAllocated->set(m_block->atomNumber(p));
536 }
537
538 inline void MarkedBlock::Handle::clearNewlyAllocated(const void* p)
539 {
540     m_newlyAllocated->clear(m_block->atomNumber(p));
541 }
542
543 inline bool MarkedBlock::Handle::clearNewlyAllocated()
544 {
545     if (m_newlyAllocated) {
546         m_newlyAllocated = nullptr;
547         return true;
548     }
549     return false;
550 }
551
552 inline bool MarkedBlock::Handle::isMarkedOrNewlyAllocated(const HeapCell* cell)
553 {
554     ASSERT(m_state == Marked);
555     return m_block->isMarked(cell) || (m_newlyAllocated && isNewlyAllocated(cell));
556 }
557
558 inline bool MarkedBlock::isMarkedOrNewlyAllocated(const HeapCell* cell)
559 {
560     ASSERT(m_handle.m_state == Marked);
561     return isMarked(cell) || (m_handle.m_newlyAllocated && m_handle.isNewlyAllocated(cell));
562 }
563
564 inline bool MarkedBlock::isMarkedOrNewlyAllocatedDuringWeakVisiting(HeapVersion heapVersion, const HeapCell* cell)
565 {
566     if (needsFlip(heapVersion))
567         return false;
568     return isMarkedOrNewlyAllocated(cell);
569 }
570
571 inline bool MarkedBlock::Handle::isLive(const HeapCell* cell)
572 {
573     assertFlipped();
574     switch (m_state) {
575     case Allocated:
576         return true;
577
578     case Marked:
579         return isMarkedOrNewlyAllocated(cell);
580
581     case New:
582     case FreeListed:
583         RELEASE_ASSERT_NOT_REACHED();
584         return false;
585     }
586
587     RELEASE_ASSERT_NOT_REACHED();
588     return false;
589 }
590
591 inline bool MarkedBlock::isAtom(const void* p)
592 {
593     ASSERT(MarkedBlock::isAtomAligned(p));
594     size_t atomNumber = this->atomNumber(p);
595     size_t firstAtom = MarkedBlock::firstAtom();
596     if (atomNumber < firstAtom) // Filters pointers into MarkedBlock metadata.
597         return false;
598     if ((atomNumber - firstAtom) % m_handle.m_atomsPerCell) // Filters pointers into cell middles.
599         return false;
600     if (atomNumber >= m_handle.m_endAtom) // Filters pointers into invalid cells out of the range.
601         return false;
602     return true;
603 }
604
605 inline bool MarkedBlock::Handle::isLiveCell(const void* p)
606 {
607     if (!m_block->isAtom(p))
608         return false;
609     return isLive(static_cast<const HeapCell*>(p));
610 }
611
612 template <typename Functor>
613 inline IterationStatus MarkedBlock::Handle::forEachCell(const Functor& functor)
614 {
615     HeapCell::Kind kind = m_attributes.cellKind;
616     for (size_t i = firstAtom(); i < m_endAtom; i += m_atomsPerCell) {
617         HeapCell* cell = reinterpret_cast_ptr<HeapCell*>(&m_block->atoms()[i]);
618         if (functor(cell, kind) == IterationStatus::Done)
619             return IterationStatus::Done;
620     }
621     return IterationStatus::Continue;
622 }
623
624 template <typename Functor>
625 inline IterationStatus MarkedBlock::Handle::forEachLiveCell(const Functor& functor)
626 {
627     flipIfNecessary();
628     HeapCell::Kind kind = m_attributes.cellKind;
629     for (size_t i = firstAtom(); i < m_endAtom; i += m_atomsPerCell) {
630         HeapCell* cell = reinterpret_cast_ptr<HeapCell*>(&m_block->atoms()[i]);
631         if (!isLive(cell))
632             continue;
633
634         if (functor(cell, kind) == IterationStatus::Done)
635             return IterationStatus::Done;
636     }
637     return IterationStatus::Continue;
638 }
639
640 template <typename Functor>
641 inline IterationStatus MarkedBlock::Handle::forEachDeadCell(const Functor& functor)
642 {
643     flipIfNecessary();
644     HeapCell::Kind kind = m_attributes.cellKind;
645     for (size_t i = firstAtom(); i < m_endAtom; i += m_atomsPerCell) {
646         HeapCell* cell = reinterpret_cast_ptr<HeapCell*>(&m_block->atoms()[i]);
647         if (isLive(cell))
648             continue;
649
650         if (functor(cell, kind) == IterationStatus::Done)
651             return IterationStatus::Done;
652     }
653     return IterationStatus::Continue;
654 }
655
656 inline bool MarkedBlock::Handle::needsSweeping() const
657 {
658     const_cast<MarkedBlock::Handle*>(this)->flipIfNecessary();
659     return m_state == Marked;
660 }
661
662 inline bool MarkedBlock::Handle::isAllocated() const
663 {
664     const_cast<MarkedBlock::Handle*>(this)->flipIfNecessary();
665     return m_state == Allocated;
666 }
667
668 inline bool MarkedBlock::Handle::isMarked() const
669 {
670     const_cast<MarkedBlock::Handle*>(this)->flipIfNecessary();
671     return m_state == Marked;
672 }
673
674 inline bool MarkedBlock::Handle::isFreeListed() const
675 {
676     const_cast<MarkedBlock::Handle*>(this)->flipIfNecessary();
677     return m_state == FreeListed;
678 }
679
680 inline bool MarkedBlock::hasAnyMarked() const
681 {
682     return m_biasedMarkCount != m_markCountBias;
683 }
684
685 inline void MarkedBlock::noteMarked()
686 {
687     // This is racy by design. We don't want to pay the price of an atomic increment!
688     int16_t biasedMarkCount = m_biasedMarkCount;
689     ++biasedMarkCount;
690     m_biasedMarkCount = biasedMarkCount;
691     if (UNLIKELY(!biasedMarkCount))
692         noteMarkedSlow();
693 }
694
695 } // namespace JSC
696
697 namespace WTF {
698
699 struct MarkedBlockHash : PtrHash<JSC::MarkedBlock*> {
700     static unsigned hash(JSC::MarkedBlock* const& key)
701     {
702         // Aligned VM regions tend to be monotonically increasing integers,
703         // which is a great hash function, but we have to remove the low bits,
704         // since they're always zero, which is a terrible hash function!
705         return reinterpret_cast<JSC::Bits>(key) / JSC::MarkedBlock::blockSize;
706     }
707 };
708
709 template<> struct DefaultHash<JSC::MarkedBlock*> {
710     typedef MarkedBlockHash Hash;
711 };
712
713 void printInternal(PrintStream& out, JSC::MarkedBlock::BlockState);
714
715 } // namespace WTF
716
717 #endif // MarkedBlock_h