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);
}
void initAsEmpty(uintptr_t arenaAddr = 0) {
JS_ASSERT(!(arenaAddr & ArenaMask));
first = arenaAddr + ArenaSize;
last = arenaAddr | (ArenaSize - 1);
JS_ASSERT(isEmpty());
}
bool isEmpty() const {
checkSpan();
return first > last;
@ -246,16 +239,6 @@ struct FreeSpan
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 {
JS_ASSERT(!(arenaAddr & ArenaMask));
@ -272,61 +255,6 @@ struct FreeSpan
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 {
#ifdef DEBUG
/* We do not allow spans at the end of the address space. */
@ -388,7 +316,90 @@ struct FreeSpan
}
#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. */

View File

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

View File

@ -399,15 +399,15 @@ ArenaHeader::checkSynchronizedWithFreeList() const
FreeSpan firstSpan = FreeSpan::decodeOffsets(arenaAddress(), firstFreeSpanOffsets);
if (firstSpan.isEmpty())
return;
const FreeSpan *list = zone->allocator.arenas.getFreeList(getAllocKind());
if (list->isEmpty() || firstSpan.arenaAddress() != list->arenaAddress())
const FreeList *freeList = zone->allocator.arenas.getFreeList(getAllocKind());
if (freeList->isEmpty() || firstSpan.arenaAddress() != freeList->arenaAddress())
return;
/*
* Here this arena has free things, FreeList::lists[thingKind] is not
* empty and also points to this arena. Thus they must the same.
*/
JS_ASSERT(firstSpan.isSameNonEmptySpan(list));
JS_ASSERT(freeList->isSameNonEmptySpan(firstSpan));
}
#endif
@ -1433,9 +1433,9 @@ inline void
ArenaLists::prepareForIncrementalGC(JSRuntime *rt)
{
for (size_t i = 0; i != FINALIZE_LIMIT; ++i) {
FreeSpan *headSpan = &freeLists[i];
if (!headSpan->isEmpty()) {
ArenaHeader *aheader = headSpan->arenaHeader();
FreeList *freeList = &freeLists[i];
if (!freeList->isEmpty()) {
ArenaHeader *aheader = freeList->arenaHeader();
aheader->allocatedDuringIncremental = true;
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
* allocate from it.
*/
freeLists[thingKind] = aheader->getFirstFreeSpan();
FreeSpan firstFreeSpan = aheader->getFirstFreeSpan();
freeLists[thingKind].setHead(&firstFreeSpan);
aheader->setAsFullyUsed();
if (MOZ_UNLIKELY(zone->wasGCStarted())) {
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
* only for the arena that was not fully allocated.
*/
FreeSpan freeLists[FINALIZE_LIMIT];
FreeList freeLists[FINALIZE_LIMIT];
ArenaList arenaLists[FINALIZE_LIMIT];
@ -601,10 +601,10 @@ class ArenaLists
static uintptr_t getFreeListOffset(AllocKind thingKind) {
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];
}
@ -661,11 +661,11 @@ class ArenaLists
}
void purge(AllocKind i) {
FreeSpan *headSpan = &freeLists[i];
if (!headSpan->isEmpty()) {
ArenaHeader *aheader = headSpan->arenaHeader();
aheader->setFirstFreeSpan(headSpan);
headSpan->initAsEmpty();
FreeList *freeList = &freeLists[i];
if (!freeList->isEmpty()) {
ArenaHeader *aheader = freeList->arenaHeader();
aheader->setFirstFreeSpan(freeList);
freeList->initAsEmpty();
}
}
@ -682,11 +682,11 @@ class ArenaLists
}
void copyFreeListToArena(AllocKind thingKind) {
FreeSpan *headSpan = &freeLists[thingKind];
if (!headSpan->isEmpty()) {
ArenaHeader *aheader = headSpan->arenaHeader();
FreeList *freeList = &freeLists[thingKind];
if (!freeList->isEmpty()) {
ArenaHeader *aheader = freeList->arenaHeader();
JS_ASSERT(!aheader->hasFreeThings());
aheader->setFirstFreeSpan(headSpan);
aheader->setFirstFreeSpan(freeList);
}
}
@ -701,10 +701,10 @@ class ArenaLists
void clearFreeListInArena(AllocKind kind) {
FreeSpan *headSpan = &freeLists[kind];
if (!headSpan->isEmpty()) {
ArenaHeader *aheader = headSpan->arenaHeader();
JS_ASSERT(aheader->getFirstFreeSpan().isSameNonEmptySpan(headSpan));
FreeList *freeList = &freeLists[kind];
if (!freeList->isEmpty()) {
ArenaHeader *aheader = freeList->arenaHeader();
JS_ASSERT(freeList->isSameNonEmptySpan(aheader->getFirstFreeSpan()));
aheader->setAsFullyUsed();
}
}
@ -714,16 +714,16 @@ class ArenaLists
* arena using copyToArena().
*/
bool isSynchronizedFreeList(AllocKind kind) {
FreeSpan *headSpan = &freeLists[kind];
if (headSpan->isEmpty())
FreeList *freeList = &freeLists[kind];
if (freeList->isEmpty())
return true;
ArenaHeader *aheader = headSpan->arenaHeader();
ArenaHeader *aheader = freeList->arenaHeader();
if (aheader->hasFreeThings()) {
/*
* If the arena has a free list, it must be the same as one in
* lists.
*/
JS_ASSERT(aheader->getFirstFreeSpan().isSameNonEmptySpan(headSpan));
JS_ASSERT(freeList->isSameNonEmptySpan(aheader->getFirstFreeSpan()));
return true;
}
return false;