Bug 966040 - Allocate dynamic slots for createObject and createArray in the nursery in GGC; r=jonco

This commit is contained in:
Terrence Cole 2014-01-31 11:43:44 -08:00
parent efa144a708
commit d319eb321c
8 changed files with 93 additions and 82 deletions

View File

@ -129,6 +129,37 @@ js::Nursery::isEmpty() const
return position() == currentStart_;
}
JSObject *
js::Nursery::allocateObject(JSContext *cx, size_t size, size_t numDynamic)
{
/* Attempt to allocate slots contiguously after object, if possible. */
if (numDynamic && numDynamic <= MaxNurserySlots) {
size_t totalSize = size + sizeof(HeapSlot) * numDynamic;
JSObject *obj = static_cast<JSObject *>(allocate(totalSize));
if (obj) {
obj->setInitialSlots(reinterpret_cast<HeapSlot *>(size_t(obj) + size));
return obj;
}
/* If we failed to allocate as a block, retry with out-of-line slots. */
}
HeapSlot *slots = nullptr;
if (numDynamic) {
slots = allocateHugeSlots(cx, numDynamic);
if (MOZ_UNLIKELY(!slots))
return nullptr;
}
JSObject *obj = static_cast<JSObject *>(allocate(size));
if (obj)
obj->setInitialSlots(slots);
else
freeSlots(cx, slots);
return obj;
}
void *
js::Nursery::allocate(size_t size)
{

View File

@ -79,10 +79,10 @@ class Nursery
}
/*
* Allocate and return a pointer to a new GC thing. Returns nullptr if the
* Nursery is full.
* Allocate and return a pointer to a new GC object with its |slots|
* pointer pre-filled. Returns nullptr if the Nursery is full.
*/
void *allocate(size_t size);
JSObject *allocateObject(JSContext *cx, size_t size, size_t numDynamic);
/* Allocate a slots array for the given object. */
HeapSlot *allocateSlots(JSContext *cx, JSObject *obj, uint32_t nslots);
@ -183,7 +183,7 @@ class Nursery
HugeSlotsSet hugeSlots;
/* The maximum number of slots allowed to reside inline in the nursery. */
static const size_t MaxNurserySlots = 100;
static const size_t MaxNurserySlots = 128;
/* The amount of space in the mapped nursery available to allocations. */
static const size_t NurseryChunkUsableSize = gc::ChunkSize - sizeof(gc::ChunkTrailer);
@ -253,6 +253,9 @@ class Nursery
struct TenureCountCache;
/* Common internal allocator function. */
void *allocate(size_t size);
/*
* Move the object at |src| in the Nursery to an already-allocated cell
* |dst| in Tenured.

View File

@ -35,7 +35,7 @@ JSObject *
jit::NewGCThingPar(ForkJoinContext *cx, gc::AllocKind allocKind)
{
JS_ASSERT(ForkJoinContext::current() == cx);
return js_NewGCObject<NoGC>(cx, allocKind, gc::TenuredHeap);
return js::NewGCObject<NoGC>(cx, allocKind, 0, gc::TenuredHeap);
}
bool

View File

@ -102,7 +102,7 @@ InvokeFunction(JSContext *cx, HandleObject obj0, uint32_t argc, Value *argv, Val
JSObject *
NewGCObject(JSContext *cx, gc::AllocKind allocKind, gc::InitialHeap initialHeap)
{
return js_NewGCObject<CanGC>(cx, allocKind, initialHeap);
return js::NewGCObject<CanGC>(cx, allocKind, 0, initialHeap);
}
bool

View File

@ -379,27 +379,26 @@ typedef CompartmentsIterT<GCZoneGroupIter> GCCompartmentGroupIter;
* Attempt to allocate a new GC thing out of the nursery. If there is not enough
* room in the nursery or there is an OOM, this method will return nullptr.
*/
template <typename T, AllowGC allowGC>
inline T *
TryNewNurseryGCThing(ThreadSafeContext *cxArg, size_t thingSize)
template <AllowGC allowGC>
inline JSObject *
TryNewNurseryObject(ThreadSafeContext *cxArg, size_t thingSize, size_t nDynamicSlots)
{
/* TODO: Integrate PJS with generational GC. */
JSContext *cx = cxArg->asJSContext();
JS_ASSERT(!IsAtomsCompartment(cx->compartment()));
JSRuntime *rt = cx->runtime();
Nursery &nursery = rt->gcNursery;
T *t = static_cast<T *>(nursery.allocate(thingSize));
if (t)
return t;
JSObject *obj = nursery.allocateObject(cx, thingSize, nDynamicSlots);
if (obj)
return obj;
if (allowGC && !rt->mainThread.suppressGC) {
MinorGC(cx, JS::gcreason::OUT_OF_NURSERY);
/* Exceeding gcMaxBytes while tenuring can disable the Nursery. */
if (nursery.isEnabled()) {
t = static_cast<T *>(nursery.allocate(thingSize));
JS_ASSERT(t);
return t;
JSObject *obj = nursery.allocateObject(cx, thingSize, nDynamicSlots);
JS_ASSERT(obj);
return obj;
}
}
return nullptr;
@ -459,9 +458,9 @@ CheckIncrementalZoneState(ThreadSafeContext *cx, T *t)
* in the partially initialized thing.
*/
template <typename T, AllowGC allowGC>
inline T *
AllocateObject(ThreadSafeContext *cx, AllocKind kind, InitialHeap heap)
template <AllowGC allowGC>
inline JSObject *
AllocateObject(ThreadSafeContext *cx, AllocKind kind, size_t nDynamicSlots, InitialHeap heap)
{
size_t thingSize = Arena::thingSize(kind);
@ -471,18 +470,31 @@ AllocateObject(ThreadSafeContext *cx, AllocKind kind, InitialHeap heap)
#ifdef JSGC_GENERATIONAL
if (cx->hasNursery() && ShouldNurseryAllocate(cx->nursery(), kind, heap)) {
T *t = TryNewNurseryGCThing<T, allowGC>(cx, thingSize);
if (t)
return t;
JSObject *obj = TryNewNurseryObject<allowGC>(cx, thingSize, nDynamicSlots);
if (obj)
return obj;
}
#endif
T *t = static_cast<T *>(cx->allocator()->arenas.allocateFromFreeList(kind, thingSize));
if (!t)
t = static_cast<T *>(js::gc::ArenaLists::refillFreeList<allowGC>(cx, kind));
HeapSlot *slots = nullptr;
if (nDynamicSlots) {
slots = cx->pod_malloc<HeapSlot>(nDynamicSlots);
if (MOZ_UNLIKELY(!slots))
return nullptr;
js::Debug_SetSlotRangeToCrashOnTouch(slots, nDynamicSlots);
}
CheckIncrementalZoneState(cx, t);
return t;
JSObject *obj = static_cast<JSObject *>(cx->allocator()->arenas.allocateFromFreeList(kind, thingSize));
if (!obj)
obj = static_cast<JSObject *>(js::gc::ArenaLists::refillFreeList<allowGC>(cx, kind));
if (obj)
obj->setInitialSlots(slots);
else
js_free(slots);
CheckIncrementalZoneState(cx, obj);
return obj;
}
template <typename T, AllowGC allowGC>
@ -506,6 +518,14 @@ AllocateNonObject(ThreadSafeContext *cx)
} /* namespace gc */
template <js::AllowGC allowGC>
inline JSObject *
NewGCObject(js::ThreadSafeContext *cx, js::gc::AllocKind kind, size_t nDynamicSlots, js::gc::InitialHeap heap)
{
JS_ASSERT(kind >= js::gc::FINALIZE_OBJECT0 && kind <= js::gc::FINALIZE_OBJECT_LAST);
return js::gc::AllocateObject<allowGC>(cx, kind, nDynamicSlots, heap);
}
template <js::AllowGC allowGC>
inline jit::JitCode *
NewJitCode(js::ThreadSafeContext *cx)
@ -522,14 +542,6 @@ NewTypeObject(js::ThreadSafeContext *cx)
} /* namespace js */
template <js::AllowGC allowGC>
inline JSObject *
js_NewGCObject(js::ThreadSafeContext *cx, js::gc::AllocKind kind, js::gc::InitialHeap heap)
{
JS_ASSERT(kind >= js::gc::FINALIZE_OBJECT0 && kind <= js::gc::FINALIZE_OBJECT_LAST);
return js::gc::AllocateObject<JSObject, allowGC>(cx, kind, heap);
}
template <js::AllowGC allowGC>
inline JSString *
js_NewGCString(js::ThreadSafeContext *cx)

View File

@ -499,31 +499,15 @@ JSObject::create(js::ExclusiveContext *cx, js::gc::AllocKind kind, js::gc::Initi
JS_ASSERT_IF(type->clasp()->finalize, heap == js::gc::TenuredHeap);
JS_ASSERT_IF(extantSlots, dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan()));
js::HeapSlot *slots = extantSlots;
if (!slots) {
size_t nDynamicSlots = dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan());
if (nDynamicSlots) {
slots = cx->pod_malloc<js::HeapSlot>(nDynamicSlots);
if (!slots)
return nullptr;
js::Debug_SetSlotRangeToCrashOnTouch(slots, nDynamicSlots);
}
}
JSObject *obj = js_NewGCObject<js::CanGC>(cx, kind, heap);
if (!obj) {
js_free(slots);
size_t nDynamicSlots = extantSlots ? 0 : dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan());
JSObject *obj = js::NewGCObject<js::CanGC>(cx, kind, nDynamicSlots, heap);
if (!obj)
return nullptr;
}
#ifdef JSGC_GENERATIONAL
if (slots && heap != js::gc::TenuredHeap)
cx->asJSContext()->runtime()->gcNursery.notifyInitialSlots(obj, slots);
#endif
obj->shape_.init(shape);
obj->type_.init(type);
obj->slots = slots;
if (extantSlots)
obj->slots = extantSlots;
obj->elements = js::emptyObjectElements;
const js::Class *clasp = type->clasp();
@ -553,37 +537,15 @@ JSObject::createArray(js::ExclusiveContext *cx, js::gc::AllocKind kind, js::gc::
* named properties stored in those fixed slots.
*/
JS_ASSERT(shape->numFixedSlots() == 0);
/*
* The array initially stores its elements inline, there must be enough
* space for an elements header.
*/
JS_ASSERT(js::gc::GetGCKindSlots(kind) >= js::ObjectElements::VALUES_PER_HEADER);
js::HeapSlot *slots = nullptr;
if (size_t nDynamicSlots = dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan())) {
slots = cx->pod_malloc<js::HeapSlot>(nDynamicSlots);
if (!slots)
return nullptr;
js::Debug_SetSlotRangeToCrashOnTouch(slots, nDynamicSlots);
}
JSObject *obj = js_NewGCObject<js::CanGC>(cx, kind, heap);
if (!obj) {
js_free(slots);
size_t nDynamicSlots = dynamicSlotsCount(0, shape->slotSpan());
JSObject *obj = js::NewGCObject<js::CanGC>(cx, kind, nDynamicSlots, heap);
if (!obj)
return nullptr;
}
#ifdef JSGC_GENERATIONAL
if (slots && heap != js::gc::TenuredHeap)
cx->asJSContext()->runtime()->gcNursery.notifyInitialSlots(obj, slots);
#endif
uint32_t capacity = js::gc::GetGCKindSlots(kind) - js::ObjectElements::VALUES_PER_HEADER;
obj->shape_.init(shape);
obj->type_.init(type);
obj->slots = slots;
obj->setFixedElements();
new (obj->getElementsHeader()) js::ObjectElements(capacity, length);

View File

@ -1521,6 +1521,9 @@ class ObjectImpl : public gc::BarrieredCell<ObjectImpl>
return privateRef(nfixed);
}
/* GC Accessors */
void setInitialSlots(HeapSlot *newSlots) { slots = newSlots; }
/* JIT Accessors */
static size_t offsetOfShape() { return offsetof(ObjectImpl, shape_); }
HeapPtrShape *addressOfShape() { return &shape_; }

View File

@ -58,7 +58,7 @@ NewObjectCache::newObjectFromHit(JSContext *cx, EntryIndex entry_, js::gc::Initi
if (cx->runtime()->upcomingZealousGC())
return nullptr;
JSObject *obj = js_NewGCObject<NoGC>(cx, entry->kind, heap);
JSObject *obj = js::NewGCObject<NoGC>(cx, entry->kind, 0, heap);
if (obj) {
copyCachedToObject(obj, templateObj, entry->kind);
probes::CreateObject(cx, obj);