bug 656261 - better GC arena layout. r=wmccloskey

This commit is contained in:
Igor Bukanov 2011-05-19 21:01:08 +02:00
Родитель ebcdca5a0d
Коммит b069a3b46a
9 изменённых файлов: 232 добавлений и 364 удалений

Просмотреть файл

@ -0,0 +1,31 @@
function build_getter(i) {
var x = [i];
return function f() { return x; }
}
function test()
{
var N = internalConst("OBJECT_MARK_STACK_LENGTH") + 2;
var o = {};
var descriptor = { enumerable: true};
for (var i = 0; i != N; ++i) {
descriptor.get = build_getter(i);
Object.defineProperty(o, i, descriptor);
}
// At this point we have an object o with N getters. Each getter in turn
// is a closure storing an array. During the GC we push to the object
// marking stack all the getters found in an object after we mark it. As N
// exceeds the size of the object marking stack, this requires to run the
// dealyed scanning for some closures to mark the array objects stored in
// them.
//
// We run the GC twice to make sure that the background finalization
// finishes before we access the objects.
gc();
gc();
for (var i = 0; i != N; ++i)
assertEq(o[i][0], i);
}
test();

Просмотреть файл

@ -45,11 +45,8 @@ struct JSCompartment;
namespace js {
namespace gc {
template <typename T> struct Arena;
struct ArenaHeader;
struct MarkingDelay;
struct Chunk;
struct FreeCell;
/*
* Live objects are marked black. How many other additional colors are available
@ -58,13 +55,8 @@ struct FreeCell;
static const uint32 BLACK = 0;
/*
* A GC cell is the base class for GC Things like JSObject, JSShortString,
* JSFunction, JSXML and for an empty cell called FreeCell. It helps avoiding
* casts from an Object to a Cell whenever we call GC related mark functions.
* Cell is not the base Class for JSString because static initialization
* (used for unitStringTables) does not work with inheritance.
* A GC cell is the base class for all GC things.
*/
struct Cell {
static const size_t CellShift = 3;
static const size_t CellSize = size_t(1) << CellShift;
@ -80,28 +72,11 @@ struct Cell {
inline JSCompartment *compartment() const;
JS_ALWAYS_INLINE js::gc::FreeCell *asFreeCell() {
return reinterpret_cast<FreeCell *>(this);
}
JS_ALWAYS_INLINE const js::gc::FreeCell *asFreeCell() const {
return reinterpret_cast<const FreeCell *>(this);
}
#ifdef DEBUG
inline bool isAligned() const;
#endif
};
struct FreeCell : Cell {
union {
FreeCell *link;
double data;
};
};
JS_STATIC_ASSERT(sizeof(FreeCell) == Cell::CellSize);
} /* namespace gc */
} /* namespace js */

Просмотреть файл

@ -119,8 +119,7 @@ JSCompartment::init()
chunk = NULL;
for (unsigned i = 0; i < FINALIZE_LIMIT; i++)
arenas[i].init();
for (unsigned i = 0; i < FINALIZE_LIMIT; i++)
freeLists.finalizables[i] = NULL;
freeLists.init();
if (!crossCompartmentWrappers.init())
return false;

Просмотреть файл

@ -141,8 +141,7 @@ FinalizeKind slotsToThingKind[] = {
JS_STATIC_ASSERT(JS_ARRAY_LENGTH(slotsToThingKind) == SLOTS_TO_THING_KIND_LIMIT);
#ifdef DEBUG
const uint8 GCThingSizeMap[] = {
JS_FRIEND_DATA(const uint8) GCThingSizeMap[] = {
sizeof(JSObject), /* FINALIZE_OBJECT0 */
sizeof(JSObject), /* FINALIZE_OBJECT0_BACKGROUND */
sizeof(JSObject_Slots2), /* FINALIZE_OBJECT2 */
@ -162,18 +161,11 @@ const uint8 GCThingSizeMap[] = {
#endif
sizeof(JSShortString), /* FINALIZE_SHORT_STRING */
sizeof(JSString), /* FINALIZE_STRING */
sizeof(JSString), /* FINALIZE_EXTERNAL_STRING */
sizeof(JSExternalString), /* FINALIZE_EXTERNAL_STRING */
};
JS_STATIC_ASSERT(JS_ARRAY_LENGTH(GCThingSizeMap) == FINALIZE_LIMIT);
JS_FRIEND_API(size_t)
ArenaHeader::getThingSize() const
{
return GCThingSizeMap[getThingKind()];
}
#endif
inline FreeCell *
ArenaHeader::getFreeList() const
{
@ -189,68 +181,54 @@ ArenaHeader::getFreeList() const
return freeList;
}
/* Initialize the arena and setup the free list. */
template<typename T>
inline FreeCell *
Arena<T>::buildFreeList()
{
T *first = &t.things[0];
T *last = &t.things[JS_ARRAY_LENGTH(t.things) - 1];
for (T *thing = first; thing != last;) {
T *following = thing + 1;
thing->asFreeCell()->link = following->asFreeCell();
thing = following;
}
last->asFreeCell()->link = NULL;
return first->asFreeCell();
}
template<typename T>
inline bool
Arena<T>::finalize(JSContext *cx)
Arena::finalize(JSContext *cx)
{
JS_ASSERT(aheader.compartment);
JS_ASSERT(!aheader.getMarkingDelay()->link);
FreeCell *nextFree = aheader.getFreeList();
uintptr_t nextFree = reinterpret_cast<uintptr_t>(aheader.getFreeList());
FreeCell *freeList = NULL;
FreeCell **tailp = &freeList;
bool allClear = true;
T *thingsEnd = &t.things[ThingsPerArena-1];
T *thing = &t.things[0];
thingsEnd++;
uintptr_t thing = thingsStart(sizeof(T));
uintptr_t end = thingsEnd();
if (!nextFree) {
nextFree = thingsEnd->asFreeCell();
nextFree = end;
} else {
JS_ASSERT(thing->asFreeCell() <= nextFree);
JS_ASSERT(nextFree < thingsEnd->asFreeCell());
JS_ASSERT(thing <= nextFree);
JS_ASSERT(nextFree < end);
}
for (;; thing++) {
if (thing->asFreeCell() == nextFree) {
if (thing == thingsEnd)
for (;; thing += sizeof(T)) {
if (thing == nextFree) {
if (thing == end)
break;
nextFree = nextFree->link;
if (!nextFree) {
nextFree = thingsEnd->asFreeCell();
FreeCell *nextLink = reinterpret_cast<FreeCell *>(nextFree)->link;
if (!nextLink) {
nextFree = end;
} else {
JS_ASSERT(thing->asFreeCell() < nextFree);
JS_ASSERT(nextFree < thingsEnd->asFreeCell());
nextFree = reinterpret_cast<uintptr_t>(nextLink);
JS_ASSERT(thing < nextFree);
JS_ASSERT(nextFree < end);
}
} else if (thing->asFreeCell()->isMarked()) {
allClear = false;
continue;
} else {
thing->finalize(cx);
T *t = reinterpret_cast<T *>(thing);
if (t->isMarked()) {
allClear = false;
continue;
}
t->finalize(cx);
#ifdef DEBUG
memset(thing, JS_FREE_PATTERN, sizeof(T));
memset(t, JS_FREE_PATTERN, sizeof(T));
#endif
}
FreeCell *t = thing->asFreeCell();
*tailp = t;
tailp = &t->link;
FreeCell *freeCell = reinterpret_cast<FreeCell *>(thing);
*tailp = freeCell;
tailp = &freeCell->link;
}
#ifdef DEBUG
@ -258,21 +236,21 @@ Arena<T>::finalize(JSContext *cx)
unsigned nfree = 0;
if (freeList) {
JS_ASSERT(tailp != &freeList);
FreeCell *t = freeList;
FreeCell *freeCell = freeList;
for (;;) {
++nfree;
if (&t->link == tailp)
if (&freeCell->link == tailp)
break;
JS_ASSERT(t < t->link);
t = t->link;
JS_ASSERT(freeCell < freeCell->link);
freeCell = freeCell->link;
}
}
if (allClear) {
JS_ASSERT(nfree == ThingsPerArena);
JS_ASSERT(freeList == static_cast<Cell *>(&t.things[0]));
JS_ASSERT(tailp == &t.things[ThingsPerArena-1].asFreeCell()->link);
JS_ASSERT(nfree == Arena::thingsPerArena(sizeof(T)));
JS_ASSERT(freeList->address() == thingsStart(sizeof(T)));
JS_ASSERT(tailp == &reinterpret_cast<FreeCell *>(end - sizeof(T))->link);
} else {
JS_ASSERT(nfree < ThingsPerArena);
JS_ASSERT(nfree < Arena::thingsPerArena(sizeof(T)));
}
#endif
*tailp = NULL;
@ -290,7 +268,7 @@ FinalizeArenas(JSContext *cx, ArenaHeader **listHeadp)
{
ArenaHeader **ap = listHeadp;
while (ArenaHeader *aheader = *ap) {
bool allClear = aheader->getArena<T>()->finalize(cx);
bool allClear = aheader->getArena()->finalize<T>(cx);
if (allClear) {
*ap = aheader->next;
aheader->chunk()->releaseArena(aheader);
@ -365,7 +343,33 @@ Chunk::withinArenasRange(Cell *cell)
return false;
}
template <typename T>
/* Turn arena cells into a free list starting from the first thing. */
template<size_t thingSize>
static inline FreeCell *
BuildFreeList(ArenaHeader *aheader)
{
uintptr_t thing = aheader->getArena()->thingsStart(thingSize);
uintptr_t end = aheader->getArena()->thingsEnd();
FreeCell *first = reinterpret_cast<FreeCell *>(thing);
FreeCell **prevp = &first->link;
for (thing += thingSize; thing != end; thing += thingSize) {
JS_ASSERT(thing < end);
FreeCell *cell = reinterpret_cast<FreeCell *>(thing);
/*
* Here prevp points to the link field of the previous cell in the
* list. Write the address of the following cell into it.
*/
*prevp = cell;
prevp = &cell->link;
}
*prevp = NULL;
return first;
}
template <size_t thingSize>
ArenaHeader *
Chunk::allocateArena(JSContext *cx, unsigned thingKind)
{
@ -374,7 +378,7 @@ Chunk::allocateArena(JSContext *cx, unsigned thingKind)
ArenaHeader *aheader = info.emptyArenaLists.getTypedFreeList(thingKind);
if (!aheader) {
aheader = info.emptyArenaLists.getOtherArena();
aheader->setFreeList(aheader->getArena<T>()->buildFreeList());
aheader->setFreeList(BuildFreeList<thingSize>(aheader));
}
JS_ASSERT(!aheader->compartment);
JS_ASSERT(!aheader->getMarkingDelay()->link);
@ -651,12 +655,13 @@ MarkArenaPtrConservatively(JSTracer *trc, ArenaHeader *aheader, uintptr_t addr)
JS_ASSERT(aheader->compartment);
JS_ASSERT(sizeof(T) == aheader->getThingSize());
uintptr_t offset = (addr & ArenaMask) - Arena<T>::FirstThingOffset;
if (offset >= Arena<T>::ThingsSpan)
uintptr_t offset = addr & ArenaMask;
uintptr_t minOffset = Arena::thingsStartOffset(sizeof(T));
if (offset < minOffset)
return CGCT_NOTARENA;
/* addr can point inside the thing so we must align the address. */
uintptr_t shift = offset % sizeof(T);
uintptr_t shift = (offset - minOffset) % sizeof(T);
T *thing = reinterpret_cast<T *>(addr - shift);
if (InFreeList(aheader, thing))
@ -1125,7 +1130,7 @@ ArenaList::searchForFreeArena()
return NULL;
}
template <typename T>
template <size_t thingSize>
inline ArenaHeader *
ArenaList::getArenaWithFreeList(JSContext *cx, unsigned thingKind)
{
@ -1191,7 +1196,7 @@ ArenaList::getArenaWithFreeList(JSContext *cx, unsigned thingKind)
* to the head of the list before the cursor to prevent checking the arena
* for the free things.
*/
ArenaHeader *aheader = chunk->allocateArena<T>(cx, thingKind);
ArenaHeader *aheader = chunk->allocateArena<thingSize>(cx, thingKind);
aheader->next = head;
if (cursor == &head)
cursor = &aheader->next;
@ -1391,7 +1396,8 @@ RefillTypedFreeList(JSContext *cx, unsigned thingKind)
if (Cell *thing = compartment->freeLists.getNext(thingKind))
return thing;
}
ArenaHeader *aheader = compartment->arenas[thingKind].getArenaWithFreeList<T>(cx, thingKind);
ArenaHeader *aheader =
compartment->arenas[thingKind].getArenaWithFreeList<sizeof(T)>(cx, thingKind);
if (aheader) {
JS_ASSERT(sizeof(T) == aheader->getThingSize());
return compartment->freeLists.populate(aheader, thingKind);
@ -1556,15 +1562,17 @@ GCMarker::delayMarkingChildren(const void *thing)
METER_UPDATE_MAX(cell->compartment()->rt->gcStats.maxunmarked, markLaterArenas);
}
template<typename T>
static void
MarkDelayedChilderen(JSTracer *trc, ArenaHeader *aheader)
MarkDelayedChildren(JSTracer *trc, ArenaHeader *aheader)
{
Arena<T> *a = aheader->getArena<T>();
T *end = &a->t.things[Arena<T>::ThingsPerArena];
for (T* thing = &a->t.things[0]; thing != end; ++thing) {
if (thing->isMarked())
js::gc::MarkChildren(trc, thing);
unsigned traceKind = GetFinalizableTraceKind(aheader->getThingKind());
size_t thingSize = aheader->getThingSize();
Arena *a = aheader->getArena();
uintptr_t end = a->thingsEnd();
for (uintptr_t thing = a->thingsStart(thingSize); thing != end; thing += thingSize) {
Cell *t = reinterpret_cast<Cell *>(thing);
if (t->isMarked())
JS_TraceChildren(trc, t, traceKind);
}
}
@ -1585,55 +1593,7 @@ GCMarker::markDelayedChildren()
JS_ASSERT(markLaterArenas);
markLaterArenas--;
#endif
switch (aheader->getThingKind()) {
case FINALIZE_OBJECT0:
case FINALIZE_OBJECT0_BACKGROUND:
MarkDelayedChilderen<JSObject>(this, aheader);
break;
case FINALIZE_OBJECT2:
case FINALIZE_OBJECT2_BACKGROUND:
MarkDelayedChilderen<JSObject_Slots2>(this, aheader);
break;
case FINALIZE_OBJECT4:
case FINALIZE_OBJECT4_BACKGROUND:
MarkDelayedChilderen<JSObject_Slots4>(this, aheader);
break;
case FINALIZE_OBJECT8:
case FINALIZE_OBJECT8_BACKGROUND:
MarkDelayedChilderen<JSObject_Slots8>(this, aheader);
break;
case FINALIZE_OBJECT12:
case FINALIZE_OBJECT12_BACKGROUND:
MarkDelayedChilderen<JSObject_Slots12>(this, aheader);
break;
case FINALIZE_OBJECT16:
case FINALIZE_OBJECT16_BACKGROUND:
MarkDelayedChilderen<JSObject_Slots16>(this, aheader);
break;
case FINALIZE_STRING:
MarkDelayedChilderen<JSString>(this, aheader);
break;
case FINALIZE_EXTERNAL_STRING:
MarkDelayedChilderen<JSExternalString>(this, aheader);
break;
case FINALIZE_SHORT_STRING:
JS_NOT_REACHED("no delayed marking");
break;
case FINALIZE_FUNCTION:
MarkDelayedChilderen<JSFunction>(this, aheader);
break;
case FINALIZE_SHAPE:
MarkDelayedChilderen<Shape>(this, aheader);
break;
#if JS_HAS_XML_SUPPORT
case FINALIZE_XML:
MarkDelayedChilderen<JSXML>(this, aheader);
break;
#endif
default:
JS_NOT_REACHED("wrong thingkind");
}
MarkDelayedChildren(this, aheader);
}
JS_ASSERT(!markLaterArenas);
}
@ -2807,22 +2767,6 @@ TraceRuntime(JSTracer *trc)
MarkRuntime(trc);
}
template<typename T>
static void
IterateArenaCells(JSContext *cx, ArenaHeader *aheader, void *data, IterateCallback callback)
{
Arena<T> *a = aheader->getArena<T>();
FreeCell *nextFree = aheader->getFreeList();
size_t traceKind = GetFinalizableTraceKind(aheader->getThingKind());
T *end = &a->t.things[Arena<T>::ThingsPerArena];
for (T *thing = &a->t.things[0]; thing != end; ++thing) {
if (thing->asFreeCell() == nextFree)
nextFree = nextFree->link;
else
(*callback)(cx, data, traceKind, (void *)thing);
}
}
static void
IterateCompartmentCells(JSContext *cx, JSCompartment *comp, uint64 traceKindMask,
void *data, IterateCallback callback)
@ -2832,57 +2776,18 @@ IterateCompartmentCells(JSContext *cx, JSCompartment *comp, uint64 traceKindMask
if (traceKindMask && !TraceKindInMask(traceKind, traceKindMask))
continue;
for (ArenaHeader *aheader = comp->arenas[thingKind].getHead();
aheader;
aheader = aheader->next)
{
switch (thingKind) {
case FINALIZE_OBJECT0:
case FINALIZE_OBJECT0_BACKGROUND:
IterateArenaCells<JSObject>(cx, aheader, data, callback);
break;
case FINALIZE_OBJECT2:
case FINALIZE_OBJECT2_BACKGROUND:
IterateArenaCells<JSObject_Slots2>(cx, aheader, data, callback);
break;
case FINALIZE_OBJECT4:
case FINALIZE_OBJECT4_BACKGROUND:
IterateArenaCells<JSObject_Slots4>(cx, aheader, data, callback);
break;
case FINALIZE_OBJECT8:
case FINALIZE_OBJECT8_BACKGROUND:
IterateArenaCells<JSObject_Slots8>(cx, aheader, data, callback);
break;
case FINALIZE_OBJECT12:
case FINALIZE_OBJECT12_BACKGROUND:
IterateArenaCells<JSObject_Slots12>(cx, aheader, data, callback);
break;
case FINALIZE_OBJECT16:
case FINALIZE_OBJECT16_BACKGROUND:
IterateArenaCells<JSObject_Slots16>(cx, aheader, data, callback);
break;
case FINALIZE_STRING:
IterateArenaCells<JSString>(cx, aheader, data, callback);
break;
case FINALIZE_EXTERNAL_STRING:
IterateArenaCells<JSExternalString>(cx, aheader, data, callback);
break;
case FINALIZE_SHORT_STRING:
IterateArenaCells<JSShortString>(cx, aheader, data, callback);
break;
case FINALIZE_FUNCTION:
IterateArenaCells<JSFunction>(cx, aheader, data, callback);
break;
case FINALIZE_SHAPE:
IterateArenaCells<Shape>(cx, aheader, data, callback);
break;
#if JS_HAS_XML_SUPPORT
case FINALIZE_XML:
IterateArenaCells<JSXML>(cx, aheader, data, callback);
break;
#endif
default:
JS_NOT_REACHED("wrong thingkind");
size_t thingSize = GCThingSizeMap[thingKind];
ArenaHeader *aheader = comp->arenas[thingKind].getHead();
for (; aheader; aheader = aheader->next) {
Arena *a = aheader->getArena();
uintptr_t end = a->thingsEnd();
FreeCell *nextFree = aheader->getFreeList();
for (uintptr_t thing = a->thingsStart(thingSize); thing != end; thing += thingSize) {
Cell *t = reinterpret_cast<Cell *>(thing);
if (t == nextFree)
nextFree = nextFree->link;
else
(*callback)(cx, data, traceKind, t);
}
}
}

Просмотреть файл

@ -77,6 +77,9 @@ struct Shape;
namespace gc {
struct Arena;
struct MarkingDelay;
/* The kind of GC thing with a finalizer. */
enum FinalizeKind {
FINALIZE_OBJECT0,
@ -104,6 +107,8 @@ enum FinalizeKind {
FINALIZE_LIMIT
};
extern JS_FRIEND_DATA(const uint8) GCThingSizeMap[];
const size_t ArenaShift = 12;
const size_t ArenaSize = size_t(1) << ArenaShift;
const size_t ArenaMask = ArenaSize - 1;
@ -119,7 +124,19 @@ const size_t ArenaBitmapBits = ArenaCellCount;
const size_t ArenaBitmapBytes = ArenaBitmapBits / 8;
const size_t ArenaBitmapWords = ArenaBitmapBits / JS_BITS_PER_WORD;
template <typename T> struct Arena;
struct FreeCell : Cell {
FreeCell *link;
void checkLink() {
/*
* The GC things on the free lists come from one arena and the things
* on the free list are linked in ascending address order.
*/
JS_ASSERT_IF(link, arenaHeader() == link->arenaHeader());
JS_ASSERT_IF(link, this < link);
}
};
/* Every arena has a header. */
struct ArenaHeader {
@ -136,9 +153,8 @@ struct ArenaHeader {
inline uintptr_t address() const;
inline Chunk *chunk() const;
template <typename T>
Arena<T> *getArena() {
return reinterpret_cast<Arena<T> *>(address());
Arena *getArena() {
return reinterpret_cast<Arena *>(address());
}
bool hasFreeList() const {
@ -166,9 +182,9 @@ struct ArenaHeader {
inline MarkingDelay *getMarkingDelay() const;
#ifdef DEBUG
JS_FRIEND_API(size_t) getThingSize() const;
#endif
size_t getThingSize() const {
return GCThingSizeMap[getThingKind()];
}
#if defined DEBUG || defined JS_GCMETER
static size_t CountListLength(const ArenaHeader *aheader) {
@ -180,74 +196,50 @@ struct ArenaHeader {
#endif
};
template <typename T, size_t N, size_t R1, size_t R2>
struct Things {
char filler1[R1];
T things[N];
char filler[R2];
};
template <typename T, size_t N, size_t R1>
struct Things<T, N, R1, 0> {
char filler1[R1];
T things[N];
};
template <typename T, size_t N, size_t R2>
struct Things<T, N, 0, R2> {
T things[N];
char filler2[R2];
};
template <typename T, size_t N>
struct Things<T, N, 0, 0> {
T things[N];
};
template <typename T>
struct Arena {
/*
* Layout of an arena:
* An arena is 4K. We want it to have a header followed by a list of T
* objects. However, each object should be aligned to a sizeof(T)-boundary.
* To achieve this, we pad before and after the object array.
* An arena is 4K in size and 4K-aligned. It starts with the ArenaHeader
* descriptor followed by some pad bytes. The remainder of the arena is
* filled with the array of T things. The pad bytes ensure that the thing
* array ends exactly at the end of the arena.
*
* +-------------+-----+----+----+-----+----+-----+
* | ArenaHeader | pad | T0 | T1 | ... | Tn | pad |
* +-------------+-----+----+----+-----+----+-----+
* +-------------+-----+----+----+-----+----+
* | ArenaHeader | pad | T0 | T1 | ... | Tn |
* +-------------+-----+----+----+-----+----+
*
* <----------------------------------------------> = 4096 bytes
* <-----> = Filler1Size
* <-------------------> = HeaderSize
* <--------------------------> = SpaceAfterHeader
* <-----> = Filler2Size
* <----------------------------------------> = ArenaSize bytes
* <-------------------> = thingsStartOffset
*/
static const size_t Filler1Size =
tl::If< sizeof(ArenaHeader) % sizeof(T) == 0, size_t,
0,
sizeof(T) - sizeof(ArenaHeader) % sizeof(T) >::result;
static const size_t HeaderSize = sizeof(ArenaHeader) + Filler1Size;
static const size_t SpaceAfterHeader = ArenaSize - HeaderSize;
static const size_t Filler2Size = SpaceAfterHeader % sizeof(T);
static const size_t ThingsPerArena = SpaceAfterHeader / sizeof(T);
static const size_t FirstThingOffset = HeaderSize;
static const size_t ThingsSpan = ThingsPerArena * sizeof(T);
ArenaHeader aheader;
Things<T, ThingsPerArena, Filler1Size, Filler2Size> t;
uint8_t data[ArenaSize - sizeof(ArenaHeader)];
static void staticAsserts() {
/*
* Everything we store in the heap must be a multiple of the cell
* size.
*/
JS_STATIC_ASSERT(sizeof(T) % Cell::CellSize == 0);
JS_STATIC_ASSERT(offsetof(Arena<T>, t.things) % sizeof(T) == 0);
JS_STATIC_ASSERT(sizeof(Arena<T>) == ArenaSize);
JS_STATIC_ASSERT(sizeof(Arena) == ArenaSize);
}
inline FreeCell *buildFreeList();
static size_t thingsPerArena(size_t thingSize) {
JS_ASSERT(thingSize % Cell::CellSize == 0);
return (ArenaSize - sizeof(ArenaHeader)) / thingSize;
}
static size_t thingsStartOffset(size_t thingSize) {
return ArenaSize - thingsPerArena(thingSize) * thingSize;
}
uintptr_t address() const {
return aheader.address();
}
uintptr_t thingsStart(size_t thingSize) {
return address() | thingsStartOffset(thingSize);
}
uintptr_t thingsEnd() {
return address() + ArenaSize;
}
template <typename T>
bool finalize(JSContext *cx);
};
@ -259,8 +251,7 @@ struct Arena {
struct MarkingDelay {
ArenaHeader *link;
void init()
{
void init() {
link = NULL;
}
@ -268,7 +259,7 @@ struct MarkingDelay {
* To separate arenas without things to mark later from the arena at the
* marked delay stack bottom we use for the latter a special sentinel
* value. We set it to the header for the second arena in the chunk
* starting the 0 address.
* starting at the 0 address.
*/
static ArenaHeader *stackBottom() {
return reinterpret_cast<ArenaHeader *>(ArenaSize);
@ -399,7 +390,7 @@ JS_STATIC_ASSERT(ArenaBitmapBytes * ArenasPerChunk == sizeof(ChunkBitmap));
* marking state).
*/
struct Chunk {
Arena<FreeCell> arenas[ArenasPerChunk];
Arena arenas[ArenasPerChunk];
ChunkBitmap bitmap;
MarkingDelay markingDelay[ArenasPerChunk];
ChunkInfo info;
@ -424,7 +415,7 @@ struct Chunk {
bool hasAvailableArenas();
bool withinArenasRange(Cell *cell);
template <typename T>
template <size_t thingSize>
ArenaHeader *allocateArena(JSContext *cx, unsigned thingKind);
void releaseArena(ArenaHeader *aheader);
@ -464,7 +455,8 @@ Cell::chunk() const
inline bool
Cell::isAligned() const
{
uintptr_t offset = address() & ArenaMask;
/* Things ends at the arena end. */
uintptr_t offset = ArenaSize - (address() & ArenaMask);
return offset % arenaHeader()->getThingSize() == 0;
}
#endif
@ -596,7 +588,7 @@ GetGCThingTraceKind(const void *thing);
static inline JSRuntime *
GetGCThingRuntime(void *thing)
{
return reinterpret_cast<FreeCell *>(thing)->chunk()->info.runtime;
return reinterpret_cast<Cell *>(thing)->chunk()->info.runtime;
}
/* The arenas in a list have uniform kind. */
@ -651,7 +643,7 @@ class ArenaList {
inline ArenaHeader *searchForFreeArena();
template <typename T>
template <size_t thingSize>
inline ArenaHeader *getArenaWithFreeList(JSContext *cx, unsigned thingKind);
template<typename T>
@ -711,17 +703,6 @@ class ArenaList {
}
};
inline void
CheckGCFreeListLink(FreeCell *cell)
{
/*
* The GC things on the free lists come from one arena and the things on
* the free list are linked in ascending address order.
*/
JS_ASSERT_IF(cell->link, cell->arenaHeader() == cell->link->arenaHeader());
JS_ASSERT_IF(cell->link, cell < cell->link);
}
/*
* For a given arena, finalizables[thingKind] points to the next object to be
* allocated. It gets initialized, in RefillTypedFreeList, to the first free
@ -736,6 +717,11 @@ CheckGCFreeListLink(FreeCell *cell)
struct FreeLists {
FreeCell *finalizables[FINALIZE_LIMIT];
void init() {
for (size_t i = 0; i < JS_ARRAY_LENGTH(finalizables); i++)
finalizables[i] = NULL;
}
/*
* Return the free list back to the arena so the GC finalization will not
* run the finalizers over unitialized bytes from free things.
@ -780,7 +766,7 @@ struct FreeLists {
FreeCell *getNext(unsigned kind) {
FreeCell *top = finalizables[kind];
if (top) {
CheckGCFreeListLink(top);
top->checkLink();
finalizables[kind] = top->link;
}
return top;
@ -789,8 +775,7 @@ struct FreeLists {
Cell *populate(ArenaHeader *aheader, uint32 thingKind) {
JS_ASSERT(!finalizables[thingKind]);
FreeCell *cell = aheader->freeList;
JS_ASSERT(cell);
CheckGCFreeListLink(cell);
cell->checkLink();
aheader->freeList = NULL;
finalizables[thingKind] = cell->link;
return cell;

Просмотреть файл

@ -1,4 +1,4 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
@ -107,7 +107,7 @@ Mark(JSTracer *trc, T *thing)
JS_ASSERT(trc->debugPrinter || trc->debugPrintArg);
JS_ASSERT(!JSAtom::isStatic(thing));
JS_ASSERT(thing->asFreeCell()->isAligned());
JS_ASSERT(thing->isAligned());
JSRuntime *rt = trc->context->runtime;
JS_ASSERT(thing->arenaHeader()->compartment);

Просмотреть файл

@ -147,61 +147,6 @@ static const char *const GC_ARENA_NAMES[] = {
};
JS_STATIC_ASSERT(JS_ARRAY_LENGTH(GC_ARENA_NAMES) == FINALIZE_LIMIT);
template <typename T>
static inline void
GetSizeAndThings(size_t &thingSize, size_t &thingsPerArena)
{
thingSize = sizeof(T);
thingsPerArena = Arena<T>::ThingsPerArena;
}
void GetSizeAndThingsPerArena(int thingKind, size_t &thingSize, size_t &thingsPerArena)
{
switch (thingKind) {
case FINALIZE_OBJECT0:
case FINALIZE_OBJECT0_BACKGROUND:
GetSizeAndThings<JSObject>(thingSize, thingsPerArena);
break;
case FINALIZE_OBJECT2:
case FINALIZE_OBJECT2_BACKGROUND:
GetSizeAndThings<JSObject_Slots2>(thingSize, thingsPerArena);
break;
case FINALIZE_OBJECT4:
case FINALIZE_OBJECT4_BACKGROUND:
GetSizeAndThings<JSObject_Slots4>(thingSize, thingsPerArena);
break;
case FINALIZE_OBJECT8:
case FINALIZE_OBJECT8_BACKGROUND:
GetSizeAndThings<JSObject_Slots8>(thingSize, thingsPerArena);
break;
case FINALIZE_OBJECT12:
case FINALIZE_OBJECT12_BACKGROUND:
GetSizeAndThings<JSObject_Slots12>(thingSize, thingsPerArena);
break;
case FINALIZE_OBJECT16:
case FINALIZE_OBJECT16_BACKGROUND:
GetSizeAndThings<JSObject_Slots16>(thingSize, thingsPerArena);
break;
case FINALIZE_EXTERNAL_STRING:
case FINALIZE_STRING:
GetSizeAndThings<JSString>(thingSize, thingsPerArena);
break;
case FINALIZE_SHORT_STRING:
GetSizeAndThings<JSShortString>(thingSize, thingsPerArena);
break;
case FINALIZE_FUNCTION:
GetSizeAndThings<JSFunction>(thingSize, thingsPerArena);
break;
#if JS_HAS_XML_SUPPORT
case FINALIZE_XML:
GetSizeAndThings<JSXML>(thingSize, thingsPerArena);
break;
#endif
default:
JS_NOT_REACHED("wrong kind");
}
}
void
DumpArenaStats(JSGCArenaStats *stp, FILE *fp)
{
@ -213,8 +158,8 @@ DumpArenaStats(JSGCArenaStats *stp, FILE *fp)
JSGCArenaStats *st = &stp[i];
if (st->maxarenas == 0)
continue;
size_t thingSize = 0, thingsPerArena = 0;
GetSizeAndThingsPerArena(i, thingSize, thingsPerArena);
size_t thingSize = GCThingSizeMap[i];
size_t thingsPerArena = Arena::thingsPerArena(thingSize);
fprintf(fp, "%s arenas (thing size %lu, %lu things per arena):\n",
GC_ARENA_NAMES[i], UL(thingSize), UL(thingsPerArena));

Просмотреть файл

@ -513,11 +513,11 @@ JS_STATIC_ASSERT(sizeof(JSInlineString) == sizeof(JSString));
class JSShortString : public JSInlineString
{
/* This can be any value that is a multiple of sizeof(gc::FreeCell). */
/* This can be any value that is a multiple of Cell::CellSize. */
static const size_t INLINE_EXTENSION_CHARS = sizeof(JSString::Data) / sizeof(jschar);
static void staticAsserts() {
JS_STATIC_ASSERT(INLINE_EXTENSION_CHARS % sizeof(js::gc::FreeCell) == 0);
JS_STATIC_ASSERT(INLINE_EXTENSION_CHARS % js::gc::Cell::CellSize == 0);
JS_STATIC_ASSERT(MAX_SHORT_LENGTH + 1 ==
(sizeof(JSShortString) -
offsetof(JSShortString, d.inlineStorage)) / sizeof(jschar));

Просмотреть файл

@ -1592,6 +1592,30 @@ GCParameter(JSContext *cx, uintN argc, jsval *vp)
return JS_TRUE;
}
static JSBool
InternalConst(JSContext *cx, uintN argc, jsval *vp)
{
if (argc != 1) {
JS_ReportError(cx, "the function takes exactly one argument");
return false;
}
JSString *str = JS_ValueToString(cx, vp[2]);
if (!str)
return false;
JSFlatString *flat = JS_FlattenString(cx, str);
if (!flat)
return false;
if (JS_FlatStringEqualsAscii(flat, "OBJECT_MARK_STACK_LENGTH")) {
vp[0] = UINT_TO_JSVAL(js::OBJECT_MARK_STACK_SIZE / sizeof(JSObject *));
} else {
JS_ReportError(cx, "unknown const name");
return false;
}
return true;
}
#ifdef JS_GC_ZEAL
static JSBool
GCZeal(JSContext *cx, uintN argc, jsval *vp)
@ -4803,6 +4827,7 @@ static JSFunctionSpec shell_functions[] = {
JS_FN("gczeal", GCZeal, 2,0),
JS_FN("schedulegc", ScheduleGC, 1,0),
#endif
JS_FN("internalConst", InternalConst, 1,0),
JS_FN("setDebug", SetDebug, 1,0),
JS_FN("setDebuggerHandler", SetDebuggerHandler, 1,0),
JS_FN("setThrowHook", SetThrowHook, 1,0),
@ -4929,6 +4954,9 @@ static const char *const shell_help_messages[] = {
"schedulegc(num, [compartmentGC?])\n"
" Schedule a GC to happen after num allocations",
#endif
"internalConst(name)\n"
" Query an internal constant for the engine. See InternalConst source for the\n"
" list of constant names",
"setDebug(debug) Set debug mode",
"setDebuggerHandler(f) Set handler for debugger keyword to f",
"setThrowHook(f) Set throw hook to f",