Bug 1011355 (part 2) - Add a CompactFreeSpan class. r=billm.

--HG--
extra : rebase_source : 693f7ff5abb5e3335b62c790c997f713526027f0
This commit is contained in:
Nicholas Nethercote 2014-05-15 22:16:25 -07:00
parent 55f51172ae
commit 75f2d0a39c
2 changed files with 68 additions and 48 deletions

View File

@ -148,8 +148,9 @@ const size_t ArenaBitmapWords = ArenaBitmapBits / JS_BITS_PER_WORD;
*/
class FreeSpan
{
friend class FreeList;
friend class ArenaCellIterImpl;
friend class CompactFreeSpan;
friend class FreeList;
uintptr_t first;
uintptr_t last;
@ -188,37 +189,6 @@ class FreeSpan
checkSpan(thingSize);
}
/*
* To minimize the size of the arena header the first span is encoded
* there as offsets from the arena start.
*/
static size_t encodeOffsets(size_t firstOffset, size_t lastOffset) {
static_assert(ArenaShift < 16, "Check that we can pack offsets into uint16_t.");
JS_ASSERT(firstOffset <= lastOffset);
JS_ASSERT(lastOffset < ArenaSize);
return firstOffset | (lastOffset << 16);
}
/*
* Encoded offsets for a full arena, i.e. one with an empty FreeSpan.
*/
static const size_t FullArenaOffsets = 0;
static FreeSpan decodeOffsets(uintptr_t arenaAddr, size_t offsets) {
JS_ASSERT(!(arenaAddr & ArenaMask));
FreeSpan decodedSpan;
if (offsets == FullArenaOffsets) {
decodedSpan.initAsEmpty();
} else {
size_t firstOffset = offsets & 0xFFFF;
size_t lastOffset = offsets >> 16;
JS_ASSERT(firstOffset <= lastOffset);
JS_ASSERT(lastOffset < ArenaSize);
decodedSpan.initBounds(arenaAddr + firstOffset, arenaAddr + lastOffset);
}
return decodedSpan;
}
bool isEmpty() const {
checkSpan();
return !first;
@ -247,10 +217,6 @@ class FreeSpan
}
#endif
size_t encodeAsOffsets() const {
return encodeOffsets(first & ArenaMask, last & ArenaMask);
}
size_t length(size_t thingSize) const {
checkSpan();
JS_ASSERT((last - first) % thingSize == 0);
@ -300,6 +266,57 @@ class FreeSpan
}
};
class CompactFreeSpan
{
uint16_t firstOffset_;
uint16_t lastOffset_;
public:
CompactFreeSpan(size_t firstOffset, size_t lastOffset)
: firstOffset_(firstOffset)
, lastOffset_(lastOffset)
{}
void initAsEmpty() {
firstOffset_ = 0;
lastOffset_ = 0;
}
bool operator==(const CompactFreeSpan &other) const {
return firstOffset_ == other.firstOffset_ &&
lastOffset_ == other.lastOffset_;
}
void compact(FreeSpan span) {
if (span.isEmpty()) {
initAsEmpty();
} else {
static_assert(ArenaShift < 16, "Check that we can pack offsets into uint16_t.");
uintptr_t arenaAddr = span.arenaAddress();
firstOffset_ = span.first - arenaAddr;
lastOffset_ = span.last - arenaAddr;
}
}
bool isEmpty() const {
JS_ASSERT(!!firstOffset_ == !!lastOffset_);
return !firstOffset_;
}
FreeSpan decompact(uintptr_t arenaAddr) const {
JS_ASSERT(!(arenaAddr & ArenaMask));
FreeSpan decodedSpan;
if (isEmpty()) {
decodedSpan.initAsEmpty();
} else {
JS_ASSERT(firstOffset_ <= lastOffset_);
JS_ASSERT(lastOffset_ < ArenaSize);
decodedSpan.initBounds(arenaAddr + firstOffset_, arenaAddr + lastOffset_);
}
return decodedSpan;
}
};
class FreeList
{
// Although |head| is private, it is exposed to the JITs via the
@ -395,11 +412,10 @@ struct ArenaHeader : public JS::shadow::ArenaHeader
private:
/*
* The first span of free things in the arena. We encode it as the start
* and end offsets within the arena, not as FreeSpan structure, to
* minimize the header size.
* The first span of free things in the arena. We encode it as a
* CompactFreeSpan rather than a FreeSpan to minimize the header size.
*/
size_t firstFreeSpanOffsets;
CompactFreeSpan firstFreeSpan;
/*
* One of AllocKind constants or FINALIZE_LIMIT when the arena does not
@ -463,8 +479,11 @@ struct ArenaHeader : public JS::shadow::ArenaHeader
static_assert(FINALIZE_LIMIT <= 255, "We must be able to fit the allockind into uint8_t.");
allocKind = size_t(kind);
/* The arena is initially marked as full; see allocateFromArenaInline(). */
firstFreeSpanOffsets = FreeSpan::FullArenaOffsets;
/*
* The firstFreeSpan is initially marked as empty (and thus the arena
* is marked as full). See allocateFromArenaInline().
*/
firstFreeSpan.initAsEmpty();
}
void setAsNotAllocated() {
@ -486,13 +505,13 @@ struct ArenaHeader : public JS::shadow::ArenaHeader
inline size_t getThingSize() const;
bool hasFreeThings() const {
return firstFreeSpanOffsets != FreeSpan::FullArenaOffsets;
return !firstFreeSpan.isEmpty();
}
inline bool isEmpty() const;
void setAsFullyUsed() {
firstFreeSpanOffsets = FreeSpan::FullArenaOffsets;
firstFreeSpan.initAsEmpty();
}
inline FreeSpan getFirstFreeSpan() const;
@ -900,7 +919,8 @@ ArenaHeader::isEmpty() const
JS_ASSERT(allocated());
size_t firstThingOffset = Arena::firstThingOffset(getAllocKind());
size_t lastThingOffset = ArenaSize - getThingSize();
return firstFreeSpanOffsets == FreeSpan::encodeOffsets(firstThingOffset, lastThingOffset);
const CompactFreeSpan emptyCompactSpan(firstThingOffset, lastThingOffset);
return firstFreeSpan == emptyCompactSpan;
}
FreeSpan
@ -909,14 +929,14 @@ ArenaHeader::getFirstFreeSpan() const
#ifdef DEBUG
checkSynchronizedWithFreeList();
#endif
return FreeSpan::decodeOffsets(arenaAddress(), firstFreeSpanOffsets);
return firstFreeSpan.decompact(arenaAddress());
}
void
ArenaHeader::setFirstFreeSpan(const FreeSpan *span)
{
JS_ASSERT_IF(!span->isEmpty(), span->isWithinArena(arenaAddress()));
firstFreeSpanOffsets = span->encodeAsOffsets();
firstFreeSpan.compact(*span);
}
inline ArenaHeader *

View File

@ -412,7 +412,7 @@ ArenaHeader::checkSynchronizedWithFreeList() const
if (IsBackgroundFinalized(getAllocKind()) && zone->runtimeFromAnyThread()->gc.helperThread.onBackgroundThread())
return;
FreeSpan firstSpan = FreeSpan::decodeOffsets(arenaAddress(), firstFreeSpanOffsets);
FreeSpan firstSpan = firstFreeSpan.decompact(arenaAddress());
if (firstSpan.isEmpty())
return;
const FreeList *freeList = zone->allocator.arenas.getFreeList(getAllocKind());