GC constraint solving should be parallel
[WebKit-https.git] / Source / JavaScriptCore / heap / MarkedBlockInlines.h
1 /*
2  * Copyright (C) 2016-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 #pragma once
27
28 #include "JSCell.h"
29 #include "MarkedAllocator.h"
30 #include "MarkedBlock.h"
31 #include "MarkedSpace.h"
32 #include "Operations.h"
33 #include "SuperSampler.h"
34 #include "VM.h"
35
36 namespace JSC {
37
38 inline unsigned MarkedBlock::Handle::cellsPerBlock()
39 {
40     return MarkedSpace::blockPayload / cellSize();
41 }
42
43 inline bool MarkedBlock::Handle::isNewlyAllocatedStale() const
44 {
45     return m_newlyAllocatedVersion != space()->newlyAllocatedVersion();
46 }
47
48 inline bool MarkedBlock::Handle::hasAnyNewlyAllocated()
49 {
50     return !isNewlyAllocatedStale();
51 }
52
53 inline Heap* MarkedBlock::heap() const
54 {
55     return &vm()->heap;
56 }
57
58 inline MarkedSpace* MarkedBlock::space() const
59 {
60     return &heap()->objectSpace();
61 }
62
63 inline MarkedSpace* MarkedBlock::Handle::space() const
64 {
65     return &heap()->objectSpace();
66 }
67
68 inline bool MarkedBlock::marksConveyLivenessDuringMarking(HeapVersion markingVersion)
69 {
70     return marksConveyLivenessDuringMarking(m_markingVersion, markingVersion);
71 }
72
73 inline bool MarkedBlock::marksConveyLivenessDuringMarking(HeapVersion myMarkingVersion, HeapVersion markingVersion)
74 {
75     // This returns true if any of these is true:
76     // - We just created the block and so the bits are clear already.
77     // - This block has objects marked during the last GC, and so its version was up-to-date just
78     //   before the current collection did beginMarking(). This means that any objects that have 
79     //   their mark bit set are valid objects that were never deleted, and so are candidates for
80     //   marking in any conservative scan. Using our jargon, they are "live".
81     // - We did ~2^32 collections and rotated the version back to null, so we needed to hard-reset
82     //   everything. If the marks had been stale, we would have cleared them. So, we can be sure that
83     //   any set mark bit reflects objects marked during last GC, i.e. "live" objects.
84     // It would be absurd to use this method when not collecting, since this special "one version
85     // back" state only makes sense when we're in a concurrent collection and have to be
86     // conservative.
87     ASSERT(space()->isMarking());
88     if (heap()->collectionScope() != CollectionScope::Full)
89         return false;
90     return myMarkingVersion == MarkedSpace::nullVersion
91         || MarkedSpace::nextVersion(myMarkingVersion) == markingVersion;
92 }
93
94 ALWAYS_INLINE bool MarkedBlock::Handle::isLive(HeapVersion markingVersion, HeapVersion newlyAllocatedVersion, bool isMarking, const HeapCell* cell)
95 {
96     if (allocator()->isAllocated(NoLockingNecessary, this))
97         return true;
98     
99     // We need to do this while holding the lock because marks might be stale. In that case, newly
100     // allocated will not yet be valid. Consider this interleaving.
101     // 
102     // One thread is doing this:
103     //
104     // 1) IsLiveChecksNewlyAllocated: We check if newly allocated is valid. If it is valid, and the bit is
105     //    set, we return true. Let's assume that this executes atomically. It doesn't have to in general,
106     //    but we can assume that for the purpose of seeing this bug.
107     //
108     // 2) IsLiveChecksMarks: Having failed that, we check the mark bits. This step implies the rest of
109     //    this function. It happens under a lock so it's atomic.
110     //
111     // Another thread is doing:
112     //
113     // 1) AboutToMarkSlow: This is the entire aboutToMarkSlow function, and let's say it's atomic. It
114     //    sorta is since it holds a lock, but that doesn't actually make it atomic with respect to
115     //    IsLiveChecksNewlyAllocated, since that does not hold a lock in our scenario.
116     //
117     // The harmful interleaving happens if we start out with a block that has stale mark bits that
118     // nonetheless convey liveness during marking (the off-by-one version trick). The interleaving is
119     // just:
120     //
121     // IsLiveChecksNewlyAllocated AboutToMarkSlow IsLiveChecksMarks
122     //
123     // We started with valid marks but invalid newly allocated. So, the first part doesn't think that
124     // anything is live, but dutifully drops down to the marks step. But in the meantime, we clear the
125     // mark bits and transfer their contents into newlyAllocated. So IsLiveChecksMarks also sees nothing
126     // live. Ooops!
127     //
128     // Fortunately, since this is just a read critical section, we can use a CountingLock.
129     //
130     // Probably many users of CountingLock could use its lambda-based and locker-based APIs. But here, we
131     // need to ensure that everything is ALWAYS_INLINE. It's hard to do that when using lambdas. It's
132     // more reliable to write it inline instead. Empirically, it seems like how inline this is has some
133     // impact on perf - around 2% on splay if you get it wrong.
134
135     MarkedBlock& block = this->block();
136     
137     auto count = block.m_lock.tryOptimisticFencelessRead();
138     if (count.value) {
139         Dependency fenceBefore = Dependency::fence(count.input);
140         MarkedBlock::Handle* fencedThis = fenceBefore.consume(this);
141         
142         ASSERT(!fencedThis->isFreeListed());
143         
144         HeapVersion myNewlyAllocatedVersion = fencedThis->m_newlyAllocatedVersion;
145         if (myNewlyAllocatedVersion == newlyAllocatedVersion) {
146             bool result = fencedThis->isNewlyAllocated(cell);
147             if (block.m_lock.fencelessValidate(count.value, Dependency::fence(result)))
148                 return result;
149         } else {
150             MarkedBlock& fencedBlock = *fenceBefore.consume(&block);
151             
152             HeapVersion myMarkingVersion = fencedBlock.m_markingVersion;
153             if (myMarkingVersion != markingVersion
154                 && (!isMarking || !fencedBlock.marksConveyLivenessDuringMarking(myMarkingVersion, markingVersion))) {
155                 if (block.m_lock.fencelessValidate(count.value, Dependency::fence(myMarkingVersion)))
156                     return false;
157             } else {
158                 bool result = fencedBlock.m_marks.get(block.atomNumber(cell));
159                 if (block.m_lock.fencelessValidate(count.value, Dependency::fence(result)))
160                     return result;
161             }
162         }
163     }
164     
165     auto locker = holdLock(block.m_lock);
166
167     ASSERT(!isFreeListed());
168     
169     HeapVersion myNewlyAllocatedVersion = m_newlyAllocatedVersion;
170     if (myNewlyAllocatedVersion == newlyAllocatedVersion)
171         return isNewlyAllocated(cell);
172     
173     if (block.areMarksStale(markingVersion)) {
174         if (!isMarking)
175             return false;
176         if (!block.marksConveyLivenessDuringMarking(markingVersion))
177             return false;
178     }
179     
180     return block.m_marks.get(block.atomNumber(cell));
181 }
182
183 inline bool MarkedBlock::Handle::isLiveCell(HeapVersion markingVersion, HeapVersion newlyAllocatedVersion, bool isMarking, const void* p)
184 {
185     if (!m_block->isAtom(p))
186         return false;
187     return isLive(markingVersion, newlyAllocatedVersion, isMarking, static_cast<const HeapCell*>(p));
188 }
189
190 inline bool MarkedBlock::Handle::isLive(const HeapCell* cell)
191 {
192     return isLive(space()->markingVersion(), space()->newlyAllocatedVersion(), space()->isMarking(), cell);
193 }
194
195 inline bool MarkedBlock::Handle::isLiveCell(const void* p)
196 {
197     return isLiveCell(space()->markingVersion(), space()->newlyAllocatedVersion(), space()->isMarking(), p);
198 }
199
200 // The following has to be true for specialization to kick in:
201 //
202 // sweepMode == SweepToFreeList
203 // scribbleMode == DontScribble
204 // newlyAllocatedMode == DoesNotHaveNewlyAllocated
205 // destructionMode != BlockHasDestrictorsAndCollectorIsRunning
206 //
207 // emptyMode = IsEmpty
208 //     destructionMode = DoesNotNeedDestruction
209 //         marksMode = MarksNotStale (1)
210 //         marksMode = MarksStale (2)
211 // emptyMode = NotEmpty
212 //     destructionMode = DoesNotNeedDestruction
213 //         marksMode = MarksNotStale (3)
214 //         marksMode = MarksStale (4)
215 //     destructionMode = NeedsDestruction
216 //         marksMode = MarksNotStale (5)
217 //         marksMode = MarksStale (6)
218 //
219 // Only the DoesNotNeedDestruction one should be specialized by MarkedBlock.
220
221 template<bool specialize, MarkedBlock::Handle::EmptyMode specializedEmptyMode, MarkedBlock::Handle::SweepMode specializedSweepMode, MarkedBlock::Handle::SweepDestructionMode specializedDestructionMode, MarkedBlock::Handle::ScribbleMode specializedScribbleMode, MarkedBlock::Handle::NewlyAllocatedMode specializedNewlyAllocatedMode, MarkedBlock::Handle::MarksMode specializedMarksMode, typename DestroyFunc>
222 void MarkedBlock::Handle::specializedSweep(FreeList* freeList, MarkedBlock::Handle::EmptyMode emptyMode, MarkedBlock::Handle::SweepMode sweepMode, MarkedBlock::Handle::SweepDestructionMode destructionMode, MarkedBlock::Handle::ScribbleMode scribbleMode, MarkedBlock::Handle::NewlyAllocatedMode newlyAllocatedMode, MarkedBlock::Handle::MarksMode marksMode, const DestroyFunc& destroyFunc)
223 {
224     if (specialize) {
225         emptyMode = specializedEmptyMode;
226         sweepMode = specializedSweepMode;
227         destructionMode = specializedDestructionMode;
228         scribbleMode = specializedScribbleMode;
229         newlyAllocatedMode = specializedNewlyAllocatedMode;
230         marksMode = specializedMarksMode;
231     }
232     
233     RELEASE_ASSERT(!(destructionMode == BlockHasNoDestructors && sweepMode == SweepOnly));
234     
235     SuperSamplerScope superSamplerScope(false);
236
237     MarkedBlock& block = this->block();
238     
239     if (false)
240         dataLog(RawPointer(this), "/", RawPointer(&block), ": MarkedBlock::Handle::specializedSweep!\n");
241     
242     unsigned cellSize = this->cellSize();
243     
244     VM& vm = *this->vm();
245     auto destroy = [&] (void* cell) {
246         JSCell* jsCell = static_cast<JSCell*>(cell);
247         if (!jsCell->isZapped()) {
248             destroyFunc(vm, jsCell);
249             jsCell->zap();
250         }
251     };
252     
253     m_allocator->setIsDestructible(NoLockingNecessary, this, false);
254     
255     if (Options::useBumpAllocator()
256         && emptyMode == IsEmpty
257         && newlyAllocatedMode == DoesNotHaveNewlyAllocated) {
258         
259         // This is an incredibly powerful assertion that checks the sanity of our block bits.
260         if (marksMode == MarksNotStale && !block.m_marks.isEmpty()) {
261             WTF::dataFile().atomically(
262                 [&] (PrintStream& out) {
263                     out.print("Block ", RawPointer(&block), ": marks not empty!\n");
264                     out.print("Block lock is held: ", block.m_lock.isHeld(), "\n");
265                     out.print("Marking version of block: ", block.m_markingVersion, "\n");
266                     out.print("Marking version of heap: ", space()->markingVersion(), "\n");
267                     UNREACHABLE_FOR_PLATFORM();
268                 });
269         }
270         
271         char* startOfLastCell = static_cast<char*>(cellAlign(block.atoms() + m_endAtom - 1));
272         char* payloadEnd = startOfLastCell + cellSize;
273         RELEASE_ASSERT(payloadEnd - MarkedBlock::blockSize <= bitwise_cast<char*>(&block));
274         char* payloadBegin = bitwise_cast<char*>(block.atoms() + firstAtom());
275         
276         if (sweepMode == SweepToFreeList)
277             setIsFreeListed();
278         if (space()->isMarking())
279             block.m_lock.unlock();
280         if (destructionMode != BlockHasNoDestructors) {
281             for (char* cell = payloadBegin; cell < payloadEnd; cell += cellSize)
282                 destroy(cell);
283         }
284         if (sweepMode == SweepToFreeList) {
285             if (scribbleMode == Scribble)
286                 scribble(payloadBegin, payloadEnd - payloadBegin);
287             freeList->initializeBump(payloadEnd, payloadEnd - payloadBegin);
288         }
289         if (false)
290             dataLog("Quickly swept block ", RawPointer(this), " with cell size ", cellSize, " and attributes ", m_attributes, ": ", pointerDump(freeList), "\n");
291         return;
292     }
293
294     // This produces a free list that is ordered in reverse through the block.
295     // This is fine, since the allocation code makes no assumptions about the
296     // order of the free list.
297     FreeCell* head = 0;
298     size_t count = 0;
299     uintptr_t secret;
300     cryptographicallyRandomValues(&secret, sizeof(uintptr_t));
301     bool isEmpty = true;
302     Vector<size_t> deadCells;
303     auto handleDeadCell = [&] (size_t i) {
304         HeapCell* cell = reinterpret_cast_ptr<HeapCell*>(&block.atoms()[i]);
305
306         if (destructionMode != BlockHasNoDestructors)
307             destroy(cell);
308
309         if (sweepMode == SweepToFreeList) {
310             FreeCell* freeCell = reinterpret_cast_ptr<FreeCell*>(cell);
311             if (scribbleMode == Scribble)
312                 scribble(freeCell, cellSize);
313             freeCell->setNext(head, secret);
314             head = freeCell;
315             ++count;
316         }
317     };
318     for (size_t i = firstAtom(); i < m_endAtom; i += m_atomsPerCell) {
319         if (emptyMode == NotEmpty
320             && ((marksMode == MarksNotStale && block.m_marks.get(i))
321                 || (newlyAllocatedMode == HasNewlyAllocated && m_newlyAllocated.get(i)))) {
322             isEmpty = false;
323             continue;
324         }
325         
326         if (destructionMode == BlockHasDestructorsAndCollectorIsRunning)
327             deadCells.append(i);
328         else
329             handleDeadCell(i);
330     }
331     
332     // We only want to discard the newlyAllocated bits if we're creating a FreeList,
333     // otherwise we would lose information on what's currently alive.
334     if (sweepMode == SweepToFreeList && newlyAllocatedMode == HasNewlyAllocated)
335         m_newlyAllocatedVersion = MarkedSpace::nullVersion;
336     
337     if (space()->isMarking())
338         block.m_lock.unlock();
339     
340     if (destructionMode == BlockHasDestructorsAndCollectorIsRunning) {
341         for (size_t i : deadCells)
342             handleDeadCell(i);
343     }
344
345     if (sweepMode == SweepToFreeList) {
346         freeList->initializeList(head, secret, count * cellSize);
347         setIsFreeListed();
348     } else if (isEmpty)
349         m_allocator->setIsEmpty(NoLockingNecessary, this, true);
350     if (false)
351         dataLog("Slowly swept block ", RawPointer(&block), " with cell size ", cellSize, " and attributes ", m_attributes, ": ", pointerDump(freeList), "\n");
352 }
353
354 template<typename DestroyFunc>
355 void MarkedBlock::Handle::finishSweepKnowingHeapCellType(FreeList* freeList, const DestroyFunc& destroyFunc)
356 {
357     SweepMode sweepMode = freeList ? SweepToFreeList : SweepOnly;
358     SweepDestructionMode destructionMode = this->sweepDestructionMode();
359     EmptyMode emptyMode = this->emptyMode();
360     ScribbleMode scribbleMode = this->scribbleMode();
361     NewlyAllocatedMode newlyAllocatedMode = this->newlyAllocatedMode();
362     MarksMode marksMode = this->marksMode();
363
364     auto trySpecialized = [&] () -> bool {
365         if (scribbleMode != DontScribble)
366             return false;
367         if (newlyAllocatedMode != DoesNotHaveNewlyAllocated)
368             return false;
369         if (destructionMode != BlockHasDestructors)
370             return false;
371         
372         switch (emptyMode) {
373         case IsEmpty:
374             switch (sweepMode) {
375             case SweepOnly:
376                 switch (marksMode) {
377                 case MarksNotStale:
378                     specializedSweep<true, IsEmpty, SweepOnly, BlockHasDestructors, DontScribble, DoesNotHaveNewlyAllocated, MarksNotStale>(freeList, IsEmpty, SweepOnly, BlockHasDestructors, DontScribble, DoesNotHaveNewlyAllocated, MarksNotStale, destroyFunc);
379                     return true;
380                 case MarksStale:
381                     specializedSweep<true, IsEmpty, SweepOnly, BlockHasDestructors, DontScribble, DoesNotHaveNewlyAllocated, MarksStale>(freeList, IsEmpty, SweepOnly, BlockHasDestructors, DontScribble, DoesNotHaveNewlyAllocated, MarksStale, destroyFunc);
382                     return true;
383                 }
384             case SweepToFreeList:
385                 switch (marksMode) {
386                 case MarksNotStale:
387                     specializedSweep<true, IsEmpty, SweepToFreeList, BlockHasDestructors, DontScribble, DoesNotHaveNewlyAllocated, MarksNotStale>(freeList, IsEmpty, SweepToFreeList, BlockHasDestructors, DontScribble, DoesNotHaveNewlyAllocated, MarksNotStale, destroyFunc);
388                     return true;
389                 case MarksStale:
390                     specializedSweep<true, IsEmpty, SweepToFreeList, BlockHasDestructors, DontScribble, DoesNotHaveNewlyAllocated, MarksStale>(freeList, IsEmpty, SweepToFreeList, BlockHasDestructors, DontScribble, DoesNotHaveNewlyAllocated, MarksStale, destroyFunc);
391                     return true;
392                 }
393             }
394         case NotEmpty:
395             switch (sweepMode) {
396             case SweepOnly:
397                 switch (marksMode) {
398                 case MarksNotStale:
399                     specializedSweep<true, NotEmpty, SweepOnly, BlockHasDestructors, DontScribble, DoesNotHaveNewlyAllocated, MarksNotStale>(freeList, NotEmpty, SweepOnly, BlockHasDestructors, DontScribble, DoesNotHaveNewlyAllocated, MarksNotStale, destroyFunc);
400                     return true;
401                 case MarksStale:
402                     specializedSweep<true, NotEmpty, SweepOnly, BlockHasDestructors, DontScribble, DoesNotHaveNewlyAllocated, MarksStale>(freeList, NotEmpty, SweepOnly, BlockHasDestructors, DontScribble, DoesNotHaveNewlyAllocated, MarksStale, destroyFunc);
403                     return true;
404                 }
405             case SweepToFreeList:
406                 switch (marksMode) {
407                 case MarksNotStale:
408                     specializedSweep<true, NotEmpty, SweepToFreeList, BlockHasDestructors, DontScribble, DoesNotHaveNewlyAllocated, MarksNotStale>(freeList, NotEmpty, SweepToFreeList, BlockHasDestructors, DontScribble, DoesNotHaveNewlyAllocated, MarksNotStale, destroyFunc);
409                     return true;
410                 case MarksStale:
411                     specializedSweep<true, NotEmpty, SweepToFreeList, BlockHasDestructors, DontScribble, DoesNotHaveNewlyAllocated, MarksStale>(freeList, NotEmpty, SweepToFreeList, BlockHasDestructors, DontScribble, DoesNotHaveNewlyAllocated, MarksStale, destroyFunc);
412                     return true;
413                 }
414             }
415         }
416         
417         return false;
418     };
419     
420     if (trySpecialized())
421         return;
422     
423     // The template arguments don't matter because the first one is false.
424     specializedSweep<false, IsEmpty, SweepOnly, BlockHasNoDestructors, DontScribble, HasNewlyAllocated, MarksStale>(freeList, emptyMode, sweepMode, destructionMode, scribbleMode, newlyAllocatedMode, marksMode, destroyFunc);
425 }
426
427 inline MarkedBlock::Handle::SweepDestructionMode MarkedBlock::Handle::sweepDestructionMode()
428 {
429     if (m_attributes.destruction == NeedsDestruction) {
430         if (space()->isMarking())
431             return BlockHasDestructorsAndCollectorIsRunning;
432         return BlockHasDestructors;
433     }
434     return BlockHasNoDestructors;
435 }
436
437 inline MarkedBlock::Handle::EmptyMode MarkedBlock::Handle::emptyMode()
438 {
439     // It's not obvious, but this is the only way to know if the block is empty. It's the only
440     // bit that captures these caveats:
441     // - It's true when the block is freshly allocated.
442     // - It's true if the block had been swept in the past, all destructors were called, and that
443     //   sweep proved that the block is empty.
444     return m_allocator->isEmpty(NoLockingNecessary, this) ? IsEmpty : NotEmpty;
445 }
446
447 inline MarkedBlock::Handle::ScribbleMode MarkedBlock::Handle::scribbleMode()
448 {
449     return scribbleFreeCells() ? Scribble : DontScribble;
450 }
451
452 inline MarkedBlock::Handle::NewlyAllocatedMode MarkedBlock::Handle::newlyAllocatedMode()
453 {
454     return hasAnyNewlyAllocated() ? HasNewlyAllocated : DoesNotHaveNewlyAllocated;
455 }
456
457 inline MarkedBlock::Handle::MarksMode MarkedBlock::Handle::marksMode()
458 {
459     HeapVersion markingVersion = space()->markingVersion();
460     bool marksAreUseful = !block().areMarksStale(markingVersion);
461     if (space()->isMarking())
462         marksAreUseful |= block().marksConveyLivenessDuringMarking(markingVersion);
463     return marksAreUseful ? MarksNotStale : MarksStale;
464 }
465
466 template <typename Functor>
467 inline IterationStatus MarkedBlock::Handle::forEachLiveCell(const Functor& functor)
468 {
469     // FIXME: This is not currently efficient to use in the constraint solver because isLive() grabs a
470     // lock to protect itself from concurrent calls to aboutToMarkSlow(). But we could get around this by
471     // having this function grab the lock before and after the iteration, and check if the marking version
472     // changed. If it did, just run again. Inside the loop, we only need to ensure that if a race were to
473     // happen, we will just overlook objects. I think that because of how aboutToMarkSlow() does things,
474     // a race ought to mean that it just returns false when it should have returned true - but this is
475     // something that would have to be verified carefully.
476     // https://bugs.webkit.org/show_bug.cgi?id=180315
477     
478     HeapCell::Kind kind = m_attributes.cellKind;
479     for (size_t i = firstAtom(); i < m_endAtom; i += m_atomsPerCell) {
480         HeapCell* cell = reinterpret_cast_ptr<HeapCell*>(&m_block->atoms()[i]);
481         if (!isLive(cell))
482             continue;
483
484         if (functor(cell, kind) == IterationStatus::Done)
485             return IterationStatus::Done;
486     }
487     return IterationStatus::Continue;
488 }
489
490 template <typename Functor>
491 inline IterationStatus MarkedBlock::Handle::forEachDeadCell(const Functor& functor)
492 {
493     HeapCell::Kind kind = m_attributes.cellKind;
494     for (size_t i = firstAtom(); i < m_endAtom; i += m_atomsPerCell) {
495         HeapCell* cell = reinterpret_cast_ptr<HeapCell*>(&m_block->atoms()[i]);
496         if (isLive(cell))
497             continue;
498
499         if (functor(cell, kind) == IterationStatus::Done)
500             return IterationStatus::Done;
501     }
502     return IterationStatus::Continue;
503 }
504
505 template <typename Functor>
506 inline IterationStatus MarkedBlock::Handle::forEachMarkedCell(const Functor& functor)
507 {
508     HeapCell::Kind kind = m_attributes.cellKind;
509     MarkedBlock& block = this->block();
510     bool areMarksStale = block.areMarksStale();
511     WTF::loadLoadFence();
512     if (areMarksStale)
513         return IterationStatus::Continue;
514     for (size_t i = firstAtom(); i < m_endAtom; i += m_atomsPerCell) {
515         HeapCell* cell = reinterpret_cast_ptr<HeapCell*>(&m_block->atoms()[i]);
516         if (!block.isMarkedRaw(cell))
517             continue;
518
519         if (functor(cell, kind) == IterationStatus::Done)
520             return IterationStatus::Done;
521     }
522     return IterationStatus::Continue;
523 }
524
525 } // namespace JSC
526