Bug 1645113 - Don't sweep arenas that were allocated during marking asa they cannot contain any dead cells r=sfink

The patch adds areanas allocated during marking into a separate set of arenas lists, which are not swept but are merged back into the main arena lists at the end of sweeping.

We do need to do some sweeping for newly allocated arenas on account of type inference data. I haven't looked too hard into why this is.

Differential Revision: https://phabricator.services.mozilla.com/D79334
This commit is contained in:
Jon Coppeard 2020-06-12 08:03:39 +00:00
parent 1085c45e66
commit acbcfd9cde
5 changed files with 124 additions and 52 deletions

View File

@ -555,8 +555,7 @@ TenuredCell* ArenaLists::refillFreeListAndAllocate(
maybeLock.emplace(rt);
}
ArenaList& al = arenaList(thingKind);
Arena* arena = al.takeNextArena();
Arena* arena = arenaList(thingKind).takeNextArena();
if (arena) {
// Empty arenas should be immediately freed.
MOZ_ASSERT(!arena->isEmpty());
@ -583,12 +582,19 @@ TenuredCell* ArenaLists::refillFreeListAndAllocate(
return nullptr;
}
MOZ_ASSERT(al.isCursorAtEnd());
al.insertBeforeCursor(arena);
addNewArena(arena, thingKind);
return freeLists.setArenaAndAllocate(arena, thingKind);
}
inline void ArenaLists::addNewArena(Arena* arena, AllocKind thingKind) {
ArenaList& al = zone_->isGCMarking() ? newArenasInMarkPhase(thingKind)
: arenaList(thingKind);
MOZ_ASSERT(al.isCursorAtEnd());
al.insertBeforeCursor(arena);
}
inline TenuredCell* FreeLists::setArenaAndAllocate(Arena* arena,
AllocKind kind) {
#ifdef DEBUG

View File

@ -256,6 +256,11 @@ js::gc::Arena* js::gc::ArenaLists::getFirstSweptArena(
return incrementalSweptArenas.ref().head();
}
js::gc::Arena* js::gc::ArenaLists::getFirstNewArenaInMarkPhase(
AllocKind thingKind) const {
return newArenasInMarkPhase(thingKind).head();
}
js::gc::Arena* js::gc::ArenaLists::getArenaAfterCursor(
AllocKind thingKind) const {
return arenaList(thingKind).arenaAfterCursor();
@ -308,6 +313,13 @@ void js::gc::ArenaLists::unmarkPreMarkedFreeCells() {
}
}
void js::gc::ArenaLists::mergeNewArenasInMarkPhase() {
for (auto i : AllAllocKinds()) {
arenaList(i).insertListWithCursorAtEnd(newArenasInMarkPhase(i));
newArenasInMarkPhase(i).clear();
}
}
void js::gc::ArenaLists::checkEmptyFreeLists() {
MOZ_ASSERT(freeLists().allEmpty());
}

View File

@ -260,8 +260,12 @@ class ArenaLists {
ZoneData<FreeLists> freeLists_;
/* The main list of arenas for each alloc kind. */
ArenaListData<AllAllocKindArray<ArenaList>> arenaLists_;
/* For each arena kind, a list of arenas allocated during marking. */
ArenaListData<AllAllocKindArray<ArenaList>> newArenasInMarkPhase_;
/* For each arena kind, a list of arenas remaining to be swept. */
MainThreadOrGCTaskData<AllAllocKindArray<Arena*>> arenasToSweep_;
@ -274,7 +278,9 @@ class ArenaLists {
ZoneData<Arena*> gcShapeArenasToUpdate;
ZoneData<Arena*> gcAccessorShapeArenasToUpdate;
ZoneData<Arena*> gcScriptArenasToUpdate;
ZoneData<Arena*> gcNewScriptArenasToUpdate;
ZoneData<Arena*> gcObjectGroupArenasToUpdate;
ZoneData<Arena*> gcNewObjectGroupArenasToUpdate;
// The list of empty arenas which are collected during the sweep phase and
// released at the end of sweeping every sweep group.
@ -294,6 +300,7 @@ class ArenaLists {
inline Arena* getFirstArena(AllocKind thingKind) const;
inline Arena* getFirstArenaToSweep(AllocKind thingKind) const;
inline Arena* getFirstSweptArena(AllocKind thingKind) const;
inline Arena* getFirstNewArenaInMarkPhase(AllocKind thingKind) const;
inline Arena* getArenaAfterCursor(AllocKind thingKind) const;
inline bool arenaListsAreEmpty() const;
@ -334,6 +341,9 @@ class ArenaLists {
void setParallelAllocEnabled(bool enabled);
inline void mergeNewArenasInMarkPhase();
void checkGCStateNotInUse();
void checkSweepStateNotInUse();
void checkNoArenasToUpdate();
void checkNoArenasToUpdateForKind(AllocKind kind);
@ -342,6 +352,13 @@ class ArenaLists {
ArenaList& arenaList(AllocKind i) { return arenaLists_.ref()[i]; }
const ArenaList& arenaList(AllocKind i) const { return arenaLists_.ref()[i]; }
ArenaList& newArenasInMarkPhase(AllocKind i) {
return newArenasInMarkPhase_.ref()[i];
}
const ArenaList& newArenasInMarkPhase(AllocKind i) const {
return newArenasInMarkPhase_.ref()[i];
}
ConcurrentUseState& concurrentUse(AllocKind i) {
return concurrentUseState_.ref()[i];
}
@ -366,6 +383,8 @@ class ArenaLists {
AllocKind thingKind,
ShouldCheckThresholds checkThresholds);
void addNewArena(Arena* arena, AllocKind thingKind);
friend class GCRuntime;
friend class js::Nursery;
friend class js::TenuringTracer;

View File

@ -23,43 +23,32 @@ namespace gc {
class AutoAssertEmptyNursery;
class ArenaIter {
Arena* arena;
Arena* unsweptArena;
Arena* sweptArena;
mozilla::DebugOnly<bool> initialized;
static constexpr size_t SourceCount = 4;
Arena* arena = nullptr;
Arena* sources[SourceCount] = {nullptr};
size_t index = 0;
mozilla::DebugOnly<bool> initialized = false;
public:
ArenaIter()
: arena(nullptr),
unsweptArena(nullptr),
sweptArena(nullptr),
initialized(false) {}
ArenaIter() = default;
ArenaIter(JS::Zone* zone, AllocKind kind) : initialized(false) {
init(zone, kind);
}
ArenaIter(JS::Zone* zone, AllocKind kind) { init(zone, kind); }
void init(JS::Zone* zone, AllocKind kind) {
MOZ_ASSERT(!initialized);
MOZ_ASSERT(zone);
sources[0] = zone->arenas.getFirstArena(kind);
sources[1] = zone->arenas.getFirstArenaToSweep(kind);
sources[2] = zone->arenas.getFirstSweptArena(kind);
sources[3] = zone->arenas.getFirstNewArenaInMarkPhase(kind);
initialized = true;
arena = zone->arenas.getFirstArena(kind);
unsweptArena = zone->arenas.getFirstArenaToSweep(kind);
sweptArena = zone->arenas.getFirstSweptArena(kind);
if (!unsweptArena) {
unsweptArena = sweptArena;
sweptArena = nullptr;
}
if (!arena) {
arena = unsweptArena;
unsweptArena = sweptArena;
sweptArena = nullptr;
}
settle();
}
bool done() const {
MOZ_ASSERT(initialized);
return !arena;
return index == SourceCount;
}
Arena* get() const {
@ -71,9 +60,19 @@ class ArenaIter {
MOZ_ASSERT(!done());
arena = arena->next;
if (!arena) {
arena = unsweptArena;
unsweptArena = sweptArena;
sweptArena = nullptr;
index++;
settle();
}
}
private:
void settle() {
while (index < SourceCount) {
arena = sources[index];
if (arena) {
break;
}
index++;
}
}
};

View File

@ -2678,13 +2678,16 @@ ArenaLists::ArenaLists(Zone* zone)
: zone_(zone),
freeLists_(zone),
arenaLists_(zone),
newArenasInMarkPhase_(zone),
arenasToSweep_(),
incrementalSweptArenaKind(zone, AllocKind::LIMIT),
incrementalSweptArenas(zone),
gcShapeArenasToUpdate(zone, nullptr),
gcAccessorShapeArenasToUpdate(zone, nullptr),
gcScriptArenasToUpdate(zone, nullptr),
gcNewScriptArenasToUpdate(zone, nullptr),
gcObjectGroupArenasToUpdate(zone, nullptr),
gcNewObjectGroupArenasToUpdate(zone, nullptr),
savedEmptyArenas(zone, nullptr) {
for (auto i : AllAllocKinds()) {
concurrentUse(i) = ConcurrentUse::None;
@ -2743,18 +2746,18 @@ void ArenaLists::queueForBackgroundSweep(JSFreeOp* fop,
inline void ArenaLists::queueForBackgroundSweep(AllocKind thingKind) {
MOZ_ASSERT(IsBackgroundFinalized(thingKind));
ArenaList* al = &arenaList(thingKind);
if (al->isEmpty()) {
MOZ_ASSERT(concurrentUse(thingKind) == ConcurrentUse::None);
return;
}
MOZ_ASSERT(concurrentUse(thingKind) == ConcurrentUse::None);
ArenaList* al = &arenaList(thingKind);
arenasToSweep(thingKind) = al->head();
al->clear();
concurrentUse(thingKind) = ConcurrentUse::BackgroundFinalize;
arenaList(thingKind).clear();
if (arenasToSweep(thingKind)) {
concurrentUse(thingKind) = ConcurrentUse::BackgroundFinalize;
} else {
arenaList(thingKind) = newArenasInMarkPhase(thingKind);
newArenasInMarkPhase(thingKind).clear();
}
}
/*static*/
@ -2780,7 +2783,7 @@ void ArenaLists::backgroundFinalize(JSFreeOp* fop, Arena* listHead,
// allocated before background finalization finishes; now that finalization is
// complete, we want to merge these lists back together.
ArenaLists* lists = &zone->arenas;
ArenaList* al = &lists->arenaList(thingKind);
ArenaList& al = lists->arenaList(thingKind);
// Flatten |finalizedSorted| into a regular ArenaList.
ArenaList finalized = finalizedSorted.toArenaList();
@ -2796,8 +2799,12 @@ void ArenaLists::backgroundFinalize(JSFreeOp* fop, Arena* listHead,
ConcurrentUse::BackgroundFinalize);
// Join |al| and |finalized| into a single list.
*al = finalized.insertListWithCursorAtEnd(*al);
ArenaList allocatedDuringSweep = al;
al = finalized;
al.insertListWithCursorAtEnd(lists->newArenasInMarkPhase(thingKind));
al.insertListWithCursorAtEnd(allocatedDuringSweep);
lists->newArenasInMarkPhase(thingKind).clear();
lists->arenasToSweep(thingKind) = nullptr;
}
@ -2814,11 +2821,23 @@ void ArenaLists::queueForegroundThingsForSweep() {
gcShapeArenasToUpdate = arenasToSweep(AllocKind::SHAPE);
gcAccessorShapeArenasToUpdate = arenasToSweep(AllocKind::ACCESSOR_SHAPE);
gcObjectGroupArenasToUpdate = arenasToSweep(AllocKind::OBJECT_GROUP);
gcNewObjectGroupArenasToUpdate =
newArenasInMarkPhase(AllocKind::OBJECT_GROUP).head();
gcScriptArenasToUpdate = arenasToSweep(AllocKind::SCRIPT);
gcNewScriptArenasToUpdate = newArenasInMarkPhase(AllocKind::SCRIPT).head();
}
void ArenaLists::checkGCStateNotInUse() {
// Called before and after collection to check the state is as expected.
#ifdef DEBUG
checkSweepStateNotInUse();
for (auto i : AllAllocKinds()) {
MOZ_ASSERT(newArenasInMarkPhase(i).isEmpty());
}
#endif
}
void ArenaLists::checkSweepStateNotInUse() {
// Called before and after sweeping to check the sweep state is as expected.
#ifdef DEBUG
checkNoArenasToUpdate();
MOZ_ASSERT(incrementalSweptArenaKind == AllocKind::LIMIT);
@ -2835,7 +2854,9 @@ void ArenaLists::checkNoArenasToUpdate() {
MOZ_ASSERT(!gcShapeArenasToUpdate);
MOZ_ASSERT(!gcAccessorShapeArenasToUpdate);
MOZ_ASSERT(!gcScriptArenasToUpdate);
MOZ_ASSERT(!gcNewScriptArenasToUpdate);
MOZ_ASSERT(!gcObjectGroupArenasToUpdate);
MOZ_ASSERT(!gcNewObjectGroupArenasToUpdate);
}
void ArenaLists::checkNoArenasToUpdateForKind(AllocKind kind) {
@ -2849,9 +2870,11 @@ void ArenaLists::checkNoArenasToUpdateForKind(AllocKind kind) {
break;
case AllocKind::SCRIPT:
MOZ_ASSERT(!gcScriptArenasToUpdate);
MOZ_ASSERT(!gcNewScriptArenasToUpdate);
break;
case AllocKind::OBJECT_GROUP:
MOZ_ASSERT(!gcObjectGroupArenasToUpdate);
MOZ_ASSERT(!gcNewObjectGroupArenasToUpdate);
break;
default:
break;
@ -4110,6 +4133,7 @@ bool GCRuntime::beginMarkPhase(JS::GCReason reason, AutoGCSession& session) {
*/
for (GCZonesIter zone(this); !zone.done(); zone.next()) {
zone->arenas.clearFreeLists();
zone->arenas.checkGCStateNotInUse();
}
marker.start();
@ -4586,6 +4610,7 @@ void GCRuntime::getNextSweepGroup() {
zone->setNeedsIncrementalBarrier(false);
zone->changeGCState(Zone::MarkBlackOnly, Zone::NoGC);
zone->arenas.unmarkPreMarkedFreeCells();
zone->arenas.mergeNewArenasInMarkPhase();
zone->gcGrayRoots().Clear();
zone->clearGCSliceThresholds();
}
@ -5516,10 +5541,6 @@ bool ArenaLists::foregroundFinalize(JSFreeOp* fop, AllocKind thingKind,
SortedArenaList& sweepList) {
checkNoArenasToUpdateForKind(thingKind);
if (!arenasToSweep(thingKind) && incrementalSweptArenas.ref().isEmpty()) {
return true;
}
// Arenas are released for use for new allocations as soon as the finalizers
// for that allocation kind have run. This means that a cell's finalizer can
// safely use IsAboutToBeFinalized to check other cells of the same alloc
@ -5540,9 +5561,13 @@ bool ArenaLists::foregroundFinalize(JSFreeOp* fop, AllocKind thingKind,
sweepList.extractEmpty(&savedEmptyArenas.ref());
ArenaList finalized = sweepList.toArenaList();
arenaList(thingKind) =
finalized.insertListWithCursorAtEnd(arenaList(thingKind));
ArenaList& al = arenaList(thingKind);
ArenaList allocatedDuringSweep = al;
al = sweepList.toArenaList();
al.insertListWithCursorAtEnd(newArenasInMarkPhase(thingKind));
al.insertListWithCursorAtEnd(allocatedDuringSweep);
newArenasInMarkPhase(thingKind).clear();
return true;
}
@ -5642,11 +5667,21 @@ IncrementalProgress GCRuntime::sweepTypeInformation(JSFreeOp* fop,
return NotFinished;
}
if (!SweepArenaList<BaseScript>(fop, &al.gcNewScriptArenasToUpdate.ref(),
budget)) {
return NotFinished;
}
if (!SweepArenaList<ObjectGroup>(fop, &al.gcObjectGroupArenasToUpdate.ref(),
budget)) {
return NotFinished;
}
if (!SweepArenaList<ObjectGroup>(
fop, &al.gcNewObjectGroupArenasToUpdate.ref(), budget)) {
return NotFinished;
}
// Finish sweeping type information in the zone.
{
gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::SWEEP_TYPES_END);
@ -6344,7 +6379,7 @@ void GCRuntime::finishCollection() {
zone->clearGCSliceThresholds();
zone->notifyObservingDebuggers();
zone->updateGCStartThresholds(*this, invocationKind, lock);
zone->arenas.checkSweepStateNotInUse();
zone->arenas.checkGCStateNotInUse();
}
}
@ -6437,6 +6472,7 @@ GCRuntime::IncrementalResult GCRuntime::resetIncrementalGC(
zone->changeGCState(Zone::MarkBlackOnly, Zone::NoGC);
zone->clearGCSliceThresholds();
zone->arenas.unmarkPreMarkedFreeCells();
zone->arenas.mergeNewArenasInMarkPhase();
}
{