Bug 1004790 (part 1) - Split off a new class FreeList from FreeSpan. r=billm.

This commit is contained in:
Nicholas Nethercote 2014-04-30 17:52:53 -07:00
parent 43d93ff145
commit 48deef3163
4 changed files with 115 additions and 103 deletions

View File

@ -206,13 +206,6 @@ struct FreeSpan
return FreeSpan(arenaAddr + firstOffset, arenaAddr | lastOffset); return FreeSpan(arenaAddr + firstOffset, arenaAddr | lastOffset);
} }
void initAsEmpty(uintptr_t arenaAddr = 0) {
JS_ASSERT(!(arenaAddr & ArenaMask));
first = arenaAddr + ArenaSize;
last = arenaAddr | (ArenaSize - 1);
JS_ASSERT(isEmpty());
}
bool isEmpty() const { bool isEmpty() const {
checkSpan(); checkSpan();
return first > last; return first > last;
@ -246,16 +239,6 @@ struct FreeSpan
return arenaAddressUnchecked(); return arenaAddressUnchecked();
} }
ArenaHeader *arenaHeader() const {
return reinterpret_cast<ArenaHeader *>(arenaAddress());
}
bool isSameNonEmptySpan(const FreeSpan *another) const {
JS_ASSERT(!isEmpty());
JS_ASSERT(!another->isEmpty());
return first == another->first && last == another->last;
}
bool isWithinArena(uintptr_t arenaAddr) const { bool isWithinArena(uintptr_t arenaAddr) const {
JS_ASSERT(!(arenaAddr & ArenaMask)); JS_ASSERT(!(arenaAddr & ArenaMask));
@ -272,61 +255,6 @@ struct FreeSpan
return encodeOffsets(first - arenaAddr, last & ArenaMask); return encodeOffsets(first - arenaAddr, last & ArenaMask);
} }
/* See comments before FreeSpan for details. */
MOZ_ALWAYS_INLINE void *allocate(size_t thingSize) {
JS_ASSERT(thingSize % CellSize == 0);
checkSpan();
uintptr_t thing = first;
if (thing < last) {
/* Bump-allocate from the current span. */
first = thing + thingSize;
} else if (MOZ_LIKELY(thing == last)) {
/*
* Move to the next span. We use MOZ_LIKELY as without PGO
* compilers mis-predict == here as unlikely to succeed.
*/
*this = *reinterpret_cast<FreeSpan *>(thing);
} else {
return nullptr;
}
checkSpan();
JS_EXTRA_POISON(reinterpret_cast<void *>(thing), JS_ALLOCATED_TENURED_PATTERN, thingSize);
return reinterpret_cast<void *>(thing);
}
/* A version of allocate when we know that the span is not empty. */
MOZ_ALWAYS_INLINE void *infallibleAllocate(size_t thingSize) {
JS_ASSERT(thingSize % CellSize == 0);
checkSpan();
uintptr_t thing = first;
if (thing < last) {
first = thing + thingSize;
} else {
JS_ASSERT(thing == last);
*this = *reinterpret_cast<FreeSpan *>(thing);
}
checkSpan();
JS_EXTRA_POISON(reinterpret_cast<void *>(thing), JS_ALLOCATED_TENURED_PATTERN, thingSize);
return reinterpret_cast<void *>(thing);
}
/*
* Allocate from a newly allocated arena. We do not move the free list
* from the arena. Rather we set the arena up as fully used during the
* initialization so to allocate we simply return the first thing in the
* arena and set the free list to point to the second.
*/
MOZ_ALWAYS_INLINE void *allocateFromNewArena(uintptr_t arenaAddr, size_t firstThingOffset,
size_t thingSize) {
JS_ASSERT(!(arenaAddr & ArenaMask));
uintptr_t thing = arenaAddr | firstThingOffset;
first = thing + thingSize;
last = arenaAddr | ArenaMask;
checkSpan();
JS_EXTRA_POISON(reinterpret_cast<void *>(thing), JS_ALLOCATED_TENURED_PATTERN, thingSize);
return reinterpret_cast<void *>(thing);
}
void checkSpan() const { void checkSpan() const {
#ifdef DEBUG #ifdef DEBUG
/* We do not allow spans at the end of the address space. */ /* We do not allow spans at the end of the address space. */
@ -388,7 +316,90 @@ struct FreeSpan
} }
#endif #endif
} }
};
struct FreeList : public FreeSpan
{
FreeList() {}
void initAsEmpty(uintptr_t arenaAddr = 0) {
JS_ASSERT(!(arenaAddr & ArenaMask));
first = arenaAddr + ArenaSize;
last = arenaAddr | (ArenaSize - 1);
JS_ASSERT(isEmpty());
}
void setHead(FreeSpan *span) {
first = span->first;
last = span->last;
}
ArenaHeader *arenaHeader() const {
return reinterpret_cast<ArenaHeader *>(arenaAddress());
}
#ifdef DEBUG
bool isSameNonEmptySpan(const FreeSpan &another) const {
JS_ASSERT(!isEmpty());
JS_ASSERT(!another.isEmpty());
return first == another.first && last == another.last;
}
#endif
/* See comments before FreeSpan for details. */
MOZ_ALWAYS_INLINE void *allocate(size_t thingSize) {
JS_ASSERT(thingSize % CellSize == 0);
checkSpan();
uintptr_t thing = first;
if (thing < last) {
/* Bump-allocate from the current span. */
first = thing + thingSize;
} else if (MOZ_LIKELY(thing == last)) {
/*
* Move to the next span. We use MOZ_LIKELY as without PGO
* compilers mis-predict == here as unlikely to succeed.
*/
setHead(reinterpret_cast<FreeSpan *>(thing));
} else {
return nullptr;
}
checkSpan();
JS_EXTRA_POISON(reinterpret_cast<void *>(thing), JS_ALLOCATED_TENURED_PATTERN, thingSize);
return reinterpret_cast<void *>(thing);
}
/* A version of allocate when we know that the span is not empty. */
MOZ_ALWAYS_INLINE void *infallibleAllocate(size_t thingSize) {
JS_ASSERT(thingSize % CellSize == 0);
checkSpan();
uintptr_t thing = first;
if (thing < last) {
first = thing + thingSize;
} else {
JS_ASSERT(thing == last);
setHead(reinterpret_cast<FreeSpan *>(thing));
}
checkSpan();
JS_EXTRA_POISON(reinterpret_cast<void *>(thing), JS_ALLOCATED_TENURED_PATTERN, thingSize);
return reinterpret_cast<void *>(thing);
}
/*
* Allocate from a newly allocated arena. We do not move the free list
* from the arena. Rather we set the arena up as fully used during the
* initialization so to allocate we simply return the first thing in the
* arena and set the free list to point to the second.
*/
MOZ_ALWAYS_INLINE void *allocateFromNewArena(uintptr_t arenaAddr, size_t firstThingOffset,
size_t thingSize) {
JS_ASSERT(!(arenaAddr & ArenaMask));
uintptr_t thing = arenaAddr | firstThingOffset;
first = thing + thingSize;
last = arenaAddr | ArenaMask;
checkSpan();
JS_EXTRA_POISON(reinterpret_cast<void *>(thing), JS_ALLOCATED_TENURED_PATTERN, thingSize);
return reinterpret_cast<void *>(thing);
}
}; };
/* Every arena has a header. */ /* Every arena has a header. */

View File

@ -463,9 +463,9 @@ MacroAssembler::newGCThing(Register result, Register temp, gc::AllocKind allocKi
CompileZone *zone = GetIonContext()->compartment->zone(); CompileZone *zone = GetIonContext()->compartment->zone();
// Inline FreeSpan::allocate. // Inline FreeList::allocate.
// There is always exactly one FreeSpan per allocKind per JSCompartment. // There is always exactly one FreeList per allocKind per JSCompartment.
// If a FreeSpan is replaced, its members are updated in the freeLists table, // If a FreeList is replaced, its members are updated in the freeLists table,
// which the code below always re-reads. // which the code below always re-reads.
loadPtr(AbsoluteAddress(zone->addressOfFreeListFirst(allocKind)), result); loadPtr(AbsoluteAddress(zone->addressOfFreeListFirst(allocKind)), result);
branchPtr(Assembler::BelowOrEqual, AbsoluteAddress(zone->addressOfFreeListLast(allocKind)), result, fail); branchPtr(Assembler::BelowOrEqual, AbsoluteAddress(zone->addressOfFreeListLast(allocKind)), result, fail);
@ -515,7 +515,7 @@ MacroAssembler::newGCThingPar(Register result, Register cx, Register tempReg1, R
tempReg1); tempReg1);
// Get a pointer to the relevant free list: // Get a pointer to the relevant free list:
// tempReg1 = (FreeSpan*) &tempReg1->arenas.freeLists[(allocKind)] // tempReg1 = (FreeList*) &tempReg1->arenas.freeLists[(allocKind)]
uint32_t offset = (offsetof(Allocator, arenas) + uint32_t offset = (offsetof(Allocator, arenas) +
js::gc::ArenaLists::getFreeListOffset(allocKind)); js::gc::ArenaLists::getFreeListOffset(allocKind));
addPtr(Imm32(offset), tempReg1); addPtr(Imm32(offset), tempReg1);

View File

@ -399,15 +399,15 @@ ArenaHeader::checkSynchronizedWithFreeList() const
FreeSpan firstSpan = FreeSpan::decodeOffsets(arenaAddress(), firstFreeSpanOffsets); FreeSpan firstSpan = FreeSpan::decodeOffsets(arenaAddress(), firstFreeSpanOffsets);
if (firstSpan.isEmpty()) if (firstSpan.isEmpty())
return; return;
const FreeSpan *list = zone->allocator.arenas.getFreeList(getAllocKind()); const FreeList *freeList = zone->allocator.arenas.getFreeList(getAllocKind());
if (list->isEmpty() || firstSpan.arenaAddress() != list->arenaAddress()) if (freeList->isEmpty() || firstSpan.arenaAddress() != freeList->arenaAddress())
return; return;
/* /*
* Here this arena has free things, FreeList::lists[thingKind] is not * Here this arena has free things, FreeList::lists[thingKind] is not
* empty and also points to this arena. Thus they must the same. * empty and also points to this arena. Thus they must the same.
*/ */
JS_ASSERT(firstSpan.isSameNonEmptySpan(list)); JS_ASSERT(freeList->isSameNonEmptySpan(firstSpan));
} }
#endif #endif
@ -1433,9 +1433,9 @@ inline void
ArenaLists::prepareForIncrementalGC(JSRuntime *rt) ArenaLists::prepareForIncrementalGC(JSRuntime *rt)
{ {
for (size_t i = 0; i != FINALIZE_LIMIT; ++i) { for (size_t i = 0; i != FINALIZE_LIMIT; ++i) {
FreeSpan *headSpan = &freeLists[i]; FreeList *freeList = &freeLists[i];
if (!headSpan->isEmpty()) { if (!freeList->isEmpty()) {
ArenaHeader *aheader = headSpan->arenaHeader(); ArenaHeader *aheader = freeList->arenaHeader();
aheader->allocatedDuringIncremental = true; aheader->allocatedDuringIncremental = true;
rt->gc.marker.delayMarkingArena(aheader); rt->gc.marker.delayMarkingArena(aheader);
} }
@ -1511,7 +1511,8 @@ ArenaLists::allocateFromArenaInline(Zone *zone, AllocKind thingKind)
* Move the free span stored in the arena to the free list and * Move the free span stored in the arena to the free list and
* allocate from it. * allocate from it.
*/ */
freeLists[thingKind] = aheader->getFirstFreeSpan(); FreeSpan firstFreeSpan = aheader->getFirstFreeSpan();
freeLists[thingKind].setHead(&firstFreeSpan);
aheader->setAsFullyUsed(); aheader->setAsFullyUsed();
if (MOZ_UNLIKELY(zone->wasGCStarted())) { if (MOZ_UNLIKELY(zone->wasGCStarted())) {
if (zone->needsBarrier()) { if (zone->needsBarrier()) {

View File

@ -537,7 +537,7 @@ class ArenaLists
* GC we only move the head of the of the list of spans back to the arena * GC we only move the head of the of the list of spans back to the arena
* only for the arena that was not fully allocated. * only for the arena that was not fully allocated.
*/ */
FreeSpan freeLists[FINALIZE_LIMIT]; FreeList freeLists[FINALIZE_LIMIT];
ArenaList arenaLists[FINALIZE_LIMIT]; ArenaList arenaLists[FINALIZE_LIMIT];
@ -601,10 +601,10 @@ class ArenaLists
static uintptr_t getFreeListOffset(AllocKind thingKind) { static uintptr_t getFreeListOffset(AllocKind thingKind) {
uintptr_t offset = offsetof(ArenaLists, freeLists); uintptr_t offset = offsetof(ArenaLists, freeLists);
return offset + thingKind * sizeof(FreeSpan); return offset + thingKind * sizeof(FreeList);
} }
const FreeSpan *getFreeList(AllocKind thingKind) const { const FreeList *getFreeList(AllocKind thingKind) const {
return &freeLists[thingKind]; return &freeLists[thingKind];
} }
@ -661,11 +661,11 @@ class ArenaLists
} }
void purge(AllocKind i) { void purge(AllocKind i) {
FreeSpan *headSpan = &freeLists[i]; FreeList *freeList = &freeLists[i];
if (!headSpan->isEmpty()) { if (!freeList->isEmpty()) {
ArenaHeader *aheader = headSpan->arenaHeader(); ArenaHeader *aheader = freeList->arenaHeader();
aheader->setFirstFreeSpan(headSpan); aheader->setFirstFreeSpan(freeList);
headSpan->initAsEmpty(); freeList->initAsEmpty();
} }
} }
@ -682,11 +682,11 @@ class ArenaLists
} }
void copyFreeListToArena(AllocKind thingKind) { void copyFreeListToArena(AllocKind thingKind) {
FreeSpan *headSpan = &freeLists[thingKind]; FreeList *freeList = &freeLists[thingKind];
if (!headSpan->isEmpty()) { if (!freeList->isEmpty()) {
ArenaHeader *aheader = headSpan->arenaHeader(); ArenaHeader *aheader = freeList->arenaHeader();
JS_ASSERT(!aheader->hasFreeThings()); JS_ASSERT(!aheader->hasFreeThings());
aheader->setFirstFreeSpan(headSpan); aheader->setFirstFreeSpan(freeList);
} }
} }
@ -701,10 +701,10 @@ class ArenaLists
void clearFreeListInArena(AllocKind kind) { void clearFreeListInArena(AllocKind kind) {
FreeSpan *headSpan = &freeLists[kind]; FreeList *freeList = &freeLists[kind];
if (!headSpan->isEmpty()) { if (!freeList->isEmpty()) {
ArenaHeader *aheader = headSpan->arenaHeader(); ArenaHeader *aheader = freeList->arenaHeader();
JS_ASSERT(aheader->getFirstFreeSpan().isSameNonEmptySpan(headSpan)); JS_ASSERT(freeList->isSameNonEmptySpan(aheader->getFirstFreeSpan()));
aheader->setAsFullyUsed(); aheader->setAsFullyUsed();
} }
} }
@ -714,16 +714,16 @@ class ArenaLists
* arena using copyToArena(). * arena using copyToArena().
*/ */
bool isSynchronizedFreeList(AllocKind kind) { bool isSynchronizedFreeList(AllocKind kind) {
FreeSpan *headSpan = &freeLists[kind]; FreeList *freeList = &freeLists[kind];
if (headSpan->isEmpty()) if (freeList->isEmpty())
return true; return true;
ArenaHeader *aheader = headSpan->arenaHeader(); ArenaHeader *aheader = freeList->arenaHeader();
if (aheader->hasFreeThings()) { if (aheader->hasFreeThings()) {
/* /*
* If the arena has a free list, it must be the same as one in * If the arena has a free list, it must be the same as one in
* lists. * lists.
*/ */
JS_ASSERT(aheader->getFirstFreeSpan().isSameNonEmptySpan(headSpan)); JS_ASSERT(freeList->isSameNonEmptySpan(aheader->getFirstFreeSpan()));
return true; return true;
} }
return false; return false;