REGRESSION(r249808): [GTK] Crash in JSC Config::permanentlyFreeze() on architecture...
[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-2019 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 #pragma once
23
24 #include "CellAttributes.h"
25 #include "DestructionMode.h"
26 #include "HeapCell.h"
27 #include "IterationStatus.h"
28 #include "WeakSet.h"
29 #include <algorithm>
30 #include <wtf/Atomics.h>
31 #include <wtf/Bitmap.h>
32 #include <wtf/CountingLock.h>
33 #include <wtf/HashFunctions.h>
34 #include <wtf/PageBlock.h>
35 #include <wtf/StdLibExtras.h>
36
37 namespace JSC {
38
39 class AlignedMemoryAllocator;    
40 class FreeList;
41 class Heap;
42 class JSCell;
43 class BlockDirectory;
44 class MarkedSpace;
45 class SlotVisitor;
46 class Subspace;
47
48 typedef uint32_t HeapVersion;
49
50 // A marked block is a page-aligned container for heap-allocated objects.
51 // Objects are allocated within cells of the marked block. For a given
52 // marked block, all cells have the same size. Objects smaller than the
53 // cell size may be allocated in the marked block, in which case the
54 // allocation suffers from internal fragmentation: wasted space whose
55 // size is equal to the difference between the cell size and the object
56 // size.
57 DECLARE_ALLOCATOR_WITH_HEAP_IDENTIFIER(MarkedBlock);
58 DECLARE_ALLOCATOR_WITH_HEAP_IDENTIFIER(MarkedBlockHandle);
59 class MarkedBlock {
60     WTF_MAKE_NONCOPYABLE(MarkedBlock);
61     WTF_MAKE_STRUCT_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(MarkedBlock);
62     friend class LLIntOffsetsExtractor;
63     friend struct VerifyMarked;
64
65 public:
66     class Footer;
67     class Handle;
68 private:
69     friend class Footer;
70     friend class Handle;
71 public:
72     static constexpr size_t atomSize = 16; // bytes
73
74     // Block size must be at least as large as the system page size.
75     static constexpr size_t blockSize = std::max(16 * KB, CeilingOnPageSize);
76
77     static constexpr size_t blockMask = ~(blockSize - 1); // blockSize must be a power of two.
78
79     static constexpr size_t atomsPerBlock = blockSize / atomSize;
80
81     static constexpr size_t maxNumberOfLowerTierCells = 8;
82     static_assert(maxNumberOfLowerTierCells <= 256);
83     
84     static_assert(!(MarkedBlock::atomSize & (MarkedBlock::atomSize - 1)), "MarkedBlock::atomSize must be a power of two.");
85     static_assert(!(MarkedBlock::blockSize & (MarkedBlock::blockSize - 1)), "MarkedBlock::blockSize must be a power of two.");
86     
87     struct VoidFunctor {
88         typedef void ReturnType;
89         void returnValue() { }
90     };
91     
92     class CountFunctor {
93     public:
94         typedef size_t ReturnType;
95
96         CountFunctor() : m_count(0) { }
97         void count(size_t count) const { m_count += count; }
98         ReturnType returnValue() const { return m_count; }
99
100     private:
101         // FIXME: This is mutable because we're using a functor rather than C++ lambdas.
102         // https://bugs.webkit.org/show_bug.cgi?id=159644
103         mutable ReturnType m_count;
104     };
105         
106     class Handle {
107         WTF_MAKE_NONCOPYABLE(Handle);
108         WTF_MAKE_STRUCT_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(MarkedBlockHandle);
109         friend class LLIntOffsetsExtractor;
110         friend class MarkedBlock;
111         friend struct VerifyMarked;
112     public:
113             
114         ~Handle();
115             
116         MarkedBlock& block();
117         MarkedBlock::Footer& blockFooter();
118             
119         void* cellAlign(void*);
120             
121         bool isEmpty();
122
123         void lastChanceToFinalize();
124
125         BlockDirectory* directory() const;
126         Subspace* subspace() const;
127         AlignedMemoryAllocator* alignedMemoryAllocator() const;
128         Heap* heap() const;
129         inline MarkedSpace* space() const;
130         VM& vm() const;
131         WeakSet& weakSet();
132             
133         enum SweepMode { SweepOnly, SweepToFreeList };
134
135         // Sweeping ensures that destructors get called and removes the block from the unswept
136         // set. Sweeping to free list also removes the block from the empty set, if it was in that
137         // set. Sweeping with SweepOnly may add this block to the empty set, if the block is found
138         // to be empty. The free-list being null implies SweepOnly.
139         //
140         // Note that you need to make sure that the empty bit reflects reality. If it's not set
141         // and the block is freshly created, then we'll make the mistake of running destructors in
142         // the block. If it's not set and the block has nothing marked, then we'll make the
143         // mistake of making a pop freelist rather than a bump freelist.
144         void sweep(FreeList*);
145         
146         // This is to be called by Subspace.
147         template<typename DestroyFunc>
148         void finishSweepKnowingHeapCellType(FreeList*, const DestroyFunc&);
149         
150         void unsweepWithNoNewlyAllocated();
151         
152         void shrink();
153             
154         void visitWeakSet(SlotVisitor&);
155         void reapWeakSet();
156             
157         // While allocating from a free list, MarkedBlock temporarily has bogus
158         // cell liveness data. To restore accurate cell liveness data, call one
159         // of these functions:
160         void didConsumeFreeList(); // Call this once you've allocated all the items in the free list.
161         void stopAllocating(const FreeList&);
162         void resumeAllocating(FreeList&); // Call this if you canonicalized a block for some non-collection related purpose.
163             
164         size_t cellSize();
165         inline unsigned cellsPerBlock();
166         
167         const CellAttributes& attributes() const;
168         DestructionMode destruction() const;
169         bool needsDestruction() const;
170         HeapCell::Kind cellKind() const;
171             
172         size_t markCount();
173         size_t size();
174         
175         bool isAllocated();
176         
177         bool isLive(HeapVersion markingVersion, HeapVersion newlyAllocatedVersion, bool isMarking, const HeapCell*);
178         inline bool isLiveCell(HeapVersion markingVersion, HeapVersion newlyAllocatedVersion, bool isMarking, const void*);
179
180         bool isLive(const HeapCell*);
181         bool isLiveCell(const void*);
182
183         bool isFreeListedCell(const void* target) const;
184
185         template <typename Functor> IterationStatus forEachCell(const Functor&);
186         template <typename Functor> inline IterationStatus forEachLiveCell(const Functor&);
187         template <typename Functor> inline IterationStatus forEachDeadCell(const Functor&);
188         template <typename Functor> inline IterationStatus forEachMarkedCell(const Functor&);
189             
190         JS_EXPORT_PRIVATE bool areMarksStale();
191         bool areMarksStaleForSweep();
192         
193         void assertMarksNotStale();
194             
195         bool isFreeListed() const { return m_isFreeListed; }
196         
197         unsigned index() const { return m_index; }
198         
199         void removeFromDirectory();
200         
201         void didAddToDirectory(BlockDirectory*, unsigned index);
202         void didRemoveFromDirectory();
203         
204         void* start() const { return &m_block->atoms()[0]; }
205         void* end() const { return &m_block->atoms()[m_endAtom]; }
206         bool contains(void* p) const { return start() <= p && p < end(); }
207
208         void dumpState(PrintStream&);
209         
210     private:
211         Handle(Heap&, AlignedMemoryAllocator*, void*);
212         
213         enum SweepDestructionMode { BlockHasNoDestructors, BlockHasDestructors, BlockHasDestructorsAndCollectorIsRunning };
214         enum ScribbleMode { DontScribble, Scribble };
215         enum EmptyMode { IsEmpty, NotEmpty };
216         enum NewlyAllocatedMode { HasNewlyAllocated, DoesNotHaveNewlyAllocated };
217         enum MarksMode { MarksStale, MarksNotStale };
218         
219         SweepDestructionMode sweepDestructionMode();
220         EmptyMode emptyMode();
221         ScribbleMode scribbleMode();
222         NewlyAllocatedMode newlyAllocatedMode();
223         MarksMode marksMode();
224         
225         template<bool, EmptyMode, SweepMode, SweepDestructionMode, ScribbleMode, NewlyAllocatedMode, MarksMode, typename DestroyFunc>
226         void specializedSweep(FreeList*, EmptyMode, SweepMode, SweepDestructionMode, ScribbleMode, NewlyAllocatedMode, MarksMode, const DestroyFunc&);
227         
228         void setIsFreeListed();
229         
230         unsigned m_atomsPerCell { std::numeric_limits<unsigned>::max() };
231         unsigned m_endAtom { std::numeric_limits<unsigned>::max() }; // This is a fuzzy end. Always test for < m_endAtom.
232             
233         CellAttributes m_attributes;
234         bool m_isFreeListed { false };
235         unsigned m_index { std::numeric_limits<unsigned>::max() };
236
237         AlignedMemoryAllocator* m_alignedMemoryAllocator { nullptr };
238         BlockDirectory* m_directory { nullptr };
239         WeakSet m_weakSet;
240         
241         MarkedBlock* m_block { nullptr };
242     };
243
244 private:    
245     static constexpr size_t atomAlignmentMask = atomSize - 1;
246
247     typedef char Atom[atomSize];
248
249 public:
250     class Footer {
251     public:
252         Footer(VM&, Handle&);
253         ~Footer();
254         
255     private:
256         friend class LLIntOffsetsExtractor;
257         friend class MarkedBlock;
258         
259         Handle& m_handle;
260         // m_vm must remain a pointer (instead of a reference) because JSCLLIntOffsetsExtractor
261         // will fail otherwise.
262         VM* m_vm;
263         Subspace* m_subspace;
264
265         CountingLock m_lock;
266     
267         // The actual mark count can be computed by doing: m_biasedMarkCount - m_markCountBias. Note
268         // that this count is racy. It will accurately detect whether or not exactly zero things were
269         // marked, but if N things got marked, then this may report anything in the range [1, N] (or
270         // before unbiased, it would be [1 + m_markCountBias, N + m_markCountBias].)
271         int16_t m_biasedMarkCount;
272     
273         // We bias the mark count so that if m_biasedMarkCount >= 0 then the block should be retired.
274         // We go to all this trouble to make marking a bit faster: this way, marking knows when to
275         // retire a block using a js/jns on m_biasedMarkCount.
276         //
277         // For example, if a block has room for 100 objects and retirement happens whenever 90% are
278         // live, then m_markCountBias will be -90. This way, when marking begins, this will cause us to
279         // set m_biasedMarkCount to -90 as well, since:
280         //
281         //     m_biasedMarkCount = actualMarkCount + m_markCountBias.
282         //
283         // Marking an object will increment m_biasedMarkCount. Once 90 objects get marked, we will have
284         // m_biasedMarkCount = 0, which will trigger retirement. In other words, we want to set
285         // m_markCountBias like so:
286         //
287         //     m_markCountBias = -(minMarkedBlockUtilization * cellsPerBlock)
288         //
289         // All of this also means that you can detect if any objects are marked by doing:
290         //
291         //     m_biasedMarkCount != m_markCountBias
292         int16_t m_markCountBias;
293
294         HeapVersion m_markingVersion;
295         HeapVersion m_newlyAllocatedVersion;
296
297         Bitmap<atomsPerBlock> m_marks;
298         Bitmap<atomsPerBlock> m_newlyAllocated;
299     };
300     
301 private:    
302     Footer& footer();
303     const Footer& footer() const;
304
305 public:
306     static constexpr size_t endAtom = (blockSize - sizeof(Footer)) / atomSize;
307     static constexpr size_t payloadSize = endAtom * atomSize;
308     static constexpr size_t footerSize = blockSize - payloadSize;
309
310     static_assert(payloadSize == ((blockSize - sizeof(MarkedBlock::Footer)) & ~(atomSize - 1)), "Payload size computed the alternate way should give the same result");
311     
312     static MarkedBlock::Handle* tryCreate(Heap&, AlignedMemoryAllocator*);
313         
314     Handle& handle();
315     const Handle& handle() const;
316         
317     VM& vm() const;
318     inline Heap* heap() const;
319     inline MarkedSpace* space() const;
320
321     static bool isAtomAligned(const void*);
322     static MarkedBlock* blockFor(const void*);
323     unsigned atomNumber(const void*);
324     size_t candidateAtomNumber(const void*);
325         
326     size_t markCount();
327
328     bool isMarked(const void*);
329     bool isMarked(HeapVersion markingVersion, const void*);
330     bool isMarked(const void*, Dependency);
331     bool testAndSetMarked(const void*, Dependency);
332         
333     bool isAtom(const void*);
334     void clearMarked(const void*);
335     
336     bool isNewlyAllocated(const void*);
337     void setNewlyAllocated(const void*);
338     void clearNewlyAllocated(const void*);
339     const Bitmap<atomsPerBlock>& newlyAllocated() const;
340     
341     HeapVersion newlyAllocatedVersion() const { return footer().m_newlyAllocatedVersion; }
342     
343     inline bool isNewlyAllocatedStale() const;
344     
345     inline bool hasAnyNewlyAllocated();
346     void resetAllocated();
347         
348     size_t cellSize();
349     const CellAttributes& attributes() const;
350     
351     bool hasAnyMarked() const;
352     void noteMarked();
353 #if ASSERT_ENABLED
354     void assertValidCell(VM&, HeapCell*) const;
355 #else
356     void assertValidCell(VM&, HeapCell*) const { }
357 #endif
358         
359     WeakSet& weakSet();
360
361     JS_EXPORT_PRIVATE bool areMarksStale();
362     bool areMarksStale(HeapVersion markingVersion);
363     
364     Dependency aboutToMark(HeapVersion markingVersion);
365         
366 #if ASSERT_ENABLED
367     JS_EXPORT_PRIVATE void assertMarksNotStale();
368 #else
369     void assertMarksNotStale() { }
370 #endif
371         
372     void resetMarks();
373     
374     bool isMarkedRaw(const void* p);
375     HeapVersion markingVersion() const { return footer().m_markingVersion; }
376     
377     const Bitmap<atomsPerBlock>& marks() const;
378     
379     CountingLock& lock() { return footer().m_lock; }
380     
381     Subspace* subspace() const { return footer().m_subspace; }
382
383     void populatePage() const
384     {
385         *bitwise_cast<volatile uint8_t*>(&footer());
386     }
387     
388     static constexpr size_t offsetOfFooter = endAtom * atomSize;
389
390 private:
391     MarkedBlock(VM&, Handle&);
392     ~MarkedBlock();
393     Atom* atoms();
394         
395     JS_EXPORT_PRIVATE void aboutToMarkSlow(HeapVersion markingVersion);
396     void clearHasAnyMarked();
397     
398     void noteMarkedSlow();
399     
400     inline bool marksConveyLivenessDuringMarking(HeapVersion markingVersion);
401     inline bool marksConveyLivenessDuringMarking(HeapVersion myMarkingVersion, HeapVersion markingVersion);
402 };
403
404 inline MarkedBlock::Footer& MarkedBlock::footer()
405 {
406     return *bitwise_cast<MarkedBlock::Footer*>(atoms() + endAtom);
407 }
408
409 inline const MarkedBlock::Footer& MarkedBlock::footer() const
410 {
411     return const_cast<MarkedBlock*>(this)->footer();
412 }
413
414 inline MarkedBlock::Handle& MarkedBlock::handle()
415 {
416     return footer().m_handle;
417 }
418
419 inline const MarkedBlock::Handle& MarkedBlock::handle() const
420 {
421     return const_cast<MarkedBlock*>(this)->handle();
422 }
423
424 inline MarkedBlock& MarkedBlock::Handle::block()
425 {
426     return *m_block;
427 }
428
429 inline MarkedBlock::Footer& MarkedBlock::Handle::blockFooter()
430 {
431     return block().footer();
432 }
433
434 inline MarkedBlock::Atom* MarkedBlock::atoms()
435 {
436     return reinterpret_cast<Atom*>(this);
437 }
438
439 inline bool MarkedBlock::isAtomAligned(const void* p)
440 {
441     return !(reinterpret_cast<uintptr_t>(p) & atomAlignmentMask);
442 }
443
444 inline void* MarkedBlock::Handle::cellAlign(void* p)
445 {
446     uintptr_t base = reinterpret_cast<uintptr_t>(block().atoms());
447     uintptr_t bits = reinterpret_cast<uintptr_t>(p);
448     bits -= base;
449     bits -= bits % cellSize();
450     bits += base;
451     return reinterpret_cast<void*>(bits);
452 }
453
454 inline MarkedBlock* MarkedBlock::blockFor(const void* p)
455 {
456     return reinterpret_cast<MarkedBlock*>(reinterpret_cast<uintptr_t>(p) & blockMask);
457 }
458
459 inline BlockDirectory* MarkedBlock::Handle::directory() const
460 {
461     return m_directory;
462 }
463
464 inline AlignedMemoryAllocator* MarkedBlock::Handle::alignedMemoryAllocator() const
465 {
466     return m_alignedMemoryAllocator;
467 }
468
469 inline Heap* MarkedBlock::Handle::heap() const
470 {
471     return m_weakSet.heap();
472 }
473
474 inline VM& MarkedBlock::Handle::vm() const
475 {
476     return m_weakSet.vm();
477 }
478
479 inline VM& MarkedBlock::vm() const
480 {
481     return *footer().m_vm;
482 }
483
484 inline WeakSet& MarkedBlock::Handle::weakSet()
485 {
486     return m_weakSet;
487 }
488
489 inline WeakSet& MarkedBlock::weakSet()
490 {
491     return handle().weakSet();
492 }
493
494 inline void MarkedBlock::Handle::shrink()
495 {
496     m_weakSet.shrink();
497 }
498
499 inline void MarkedBlock::Handle::visitWeakSet(SlotVisitor& visitor)
500 {
501     return m_weakSet.visit(visitor);
502 }
503
504 inline void MarkedBlock::Handle::reapWeakSet()
505 {
506     m_weakSet.reap();
507 }
508
509 inline size_t MarkedBlock::Handle::cellSize()
510 {
511     return m_atomsPerCell * atomSize;
512 }
513
514 inline size_t MarkedBlock::cellSize()
515 {
516     return handle().cellSize();
517 }
518
519 inline const CellAttributes& MarkedBlock::Handle::attributes() const
520 {
521     return m_attributes;
522 }
523
524 inline const CellAttributes& MarkedBlock::attributes() const
525 {
526     return handle().attributes();
527 }
528
529 inline bool MarkedBlock::Handle::needsDestruction() const
530 {
531     return m_attributes.destruction == NeedsDestruction;
532 }
533
534 inline DestructionMode MarkedBlock::Handle::destruction() const
535 {
536     return m_attributes.destruction;
537 }
538
539 inline HeapCell::Kind MarkedBlock::Handle::cellKind() const
540 {
541     return m_attributes.cellKind;
542 }
543
544 inline size_t MarkedBlock::Handle::markCount()
545 {
546     return m_block->markCount();
547 }
548
549 inline size_t MarkedBlock::Handle::size()
550 {
551     return markCount() * cellSize();
552 }
553
554 inline size_t MarkedBlock::candidateAtomNumber(const void* p)
555 {
556     // This function must return size_t instead of unsigned since pointer |p| is not guaranteed that this is within MarkedBlock.
557     // See MarkedBlock::isAtom which can accept out-of-bound pointers.
558     return (reinterpret_cast<uintptr_t>(p) - reinterpret_cast<uintptr_t>(this)) / atomSize;
559 }
560
561 inline unsigned MarkedBlock::atomNumber(const void* p)
562 {
563     size_t atomNumber = candidateAtomNumber(p);
564     ASSERT(atomNumber < handle().m_endAtom);
565     return atomNumber;
566 }
567
568 inline bool MarkedBlock::areMarksStale(HeapVersion markingVersion)
569 {
570     return markingVersion != footer().m_markingVersion;
571 }
572
573 inline Dependency MarkedBlock::aboutToMark(HeapVersion markingVersion)
574 {
575     HeapVersion version = footer().m_markingVersion;
576     if (UNLIKELY(version != markingVersion))
577         aboutToMarkSlow(markingVersion);
578     return Dependency::fence(version);
579 }
580
581 inline void MarkedBlock::Handle::assertMarksNotStale()
582 {
583     block().assertMarksNotStale();
584 }
585
586 inline bool MarkedBlock::isMarkedRaw(const void* p)
587 {
588     return footer().m_marks.get(atomNumber(p));
589 }
590
591 inline bool MarkedBlock::isMarked(HeapVersion markingVersion, const void* p)
592 {
593     HeapVersion version = footer().m_markingVersion;
594     if (UNLIKELY(version != markingVersion))
595         return false;
596     return footer().m_marks.get(atomNumber(p), Dependency::fence(version));
597 }
598
599 inline bool MarkedBlock::isMarked(const void* p, Dependency dependency)
600 {
601     assertMarksNotStale();
602     return footer().m_marks.get(atomNumber(p), dependency);
603 }
604
605 inline bool MarkedBlock::testAndSetMarked(const void* p, Dependency dependency)
606 {
607     assertMarksNotStale();
608     return footer().m_marks.concurrentTestAndSet(atomNumber(p), dependency);
609 }
610
611 inline const Bitmap<MarkedBlock::atomsPerBlock>& MarkedBlock::marks() const
612 {
613     return footer().m_marks;
614 }
615
616 inline bool MarkedBlock::isNewlyAllocated(const void* p)
617 {
618     return footer().m_newlyAllocated.get(atomNumber(p));
619 }
620
621 inline void MarkedBlock::setNewlyAllocated(const void* p)
622 {
623     footer().m_newlyAllocated.set(atomNumber(p));
624 }
625
626 inline void MarkedBlock::clearNewlyAllocated(const void* p)
627 {
628     footer().m_newlyAllocated.clear(atomNumber(p));
629 }
630
631 inline const Bitmap<MarkedBlock::atomsPerBlock>& MarkedBlock::newlyAllocated() const
632 {
633     return footer().m_newlyAllocated;
634 }
635
636 inline bool MarkedBlock::isAtom(const void* p)
637 {
638     ASSERT(MarkedBlock::isAtomAligned(p));
639     size_t atomNumber = candidateAtomNumber(p);
640     if (atomNumber % handle().m_atomsPerCell) // Filters pointers into cell middles.
641         return false;
642     if (atomNumber >= handle().m_endAtom) // Filters pointers into invalid cells out of the range.
643         return false;
644     return true;
645 }
646
647 template <typename Functor>
648 inline IterationStatus MarkedBlock::Handle::forEachCell(const Functor& functor)
649 {
650     HeapCell::Kind kind = m_attributes.cellKind;
651     for (size_t i = 0; i < m_endAtom; i += m_atomsPerCell) {
652         HeapCell* cell = reinterpret_cast_ptr<HeapCell*>(&m_block->atoms()[i]);
653         if (functor(i, cell, kind) == IterationStatus::Done)
654             return IterationStatus::Done;
655     }
656     return IterationStatus::Continue;
657 }
658
659 inline bool MarkedBlock::hasAnyMarked() const
660 {
661     return footer().m_biasedMarkCount != footer().m_markCountBias;
662 }
663
664 inline void MarkedBlock::noteMarked()
665 {
666     // This is racy by design. We don't want to pay the price of an atomic increment!
667     int16_t biasedMarkCount = footer().m_biasedMarkCount;
668     ++biasedMarkCount;
669     footer().m_biasedMarkCount = biasedMarkCount;
670     if (UNLIKELY(!biasedMarkCount))
671         noteMarkedSlow();
672 }
673
674 } // namespace JSC
675
676 namespace WTF {
677
678 struct MarkedBlockHash : PtrHash<JSC::MarkedBlock*> {
679     static unsigned hash(JSC::MarkedBlock* const& key)
680     {
681         // Aligned VM regions tend to be monotonically increasing integers,
682         // which is a great hash function, but we have to remove the low bits,
683         // since they're always zero, which is a terrible hash function!
684         return reinterpret_cast<uintptr_t>(key) / JSC::MarkedBlock::blockSize;
685     }
686 };
687
688 template<> struct DefaultHash<JSC::MarkedBlock*> {
689     typedef MarkedBlockHash Hash;
690 };
691
692 void printInternal(PrintStream& out, JSC::MarkedBlock::Handle::SweepMode);
693
694 } // namespace WTF