Bug 889129 - Fix performance on Splay with generational GC enabled; r=bhackett

--HG--
extra : rebase_source : bebc97112c6d7e09d42d7083e49b7bb6cd6dceae
This commit is contained in:
Terrence Cole 2013-07-01 16:22:30 -07:00
parent ac4d65e1fd
commit bf843e6c76
16 changed files with 192 additions and 13 deletions

View File

@ -11,6 +11,7 @@
#include "jscompartment.h"
#include "jsgc.h"
#include "jsinfer.h"
#include "jsutil.h"
#include "gc/GCInternals.h"
@ -349,12 +350,38 @@ js::Nursery::forwardBufferPointer(HeapSlot **pSlotsElems)
JS_ASSERT(!isInside(*pSlotsElems));
}
static void
MaybeInvalidateScriptUsedWithNew(JSRuntime *rt, types::TypeObject *type)
{
types::TypeNewScript *newScript = type->newScript();
if (!newScript)
return;
JSScript *script = newScript->fun->nonLazyScript();
if (script && script->hasIonScript()) {
for (ContextIter cx(rt); !cx.done(); cx.next())
jit::Invalidate(cx, script);
}
}
void
js::Nursery::collectToFixedPoint(MinorCollectionTracer *trc)
{
for (RelocationOverlay *p = trc->head; p; p = p->next()) {
JSObject *obj = static_cast<JSObject*>(p->forwardingAddress());
traceObject(trc, obj);
/*
* Increment tenure count and recompile the script for pre-tenuring if
* long-lived. Attempt to distinguish between tenuring because the
* object is long lived and tenuring while the nursery is still
* smaller than the working set size.
*/
if (isFullyGrown() && !obj->hasLazyType() && obj->type()->hasNewScript() &&
obj->type()->incrementTenureCount())
{
MaybeInvalidateScriptUsedWithNew(trc->runtime, obj->type());
}
}
}

View File

@ -194,6 +194,10 @@ class Nursery
return chunk(numActiveChunks_ - 1).end();
}
JS_ALWAYS_INLINE bool isFullyGrown() const {
return numActiveChunks_ == NumNurseryChunks;
}
JS_ALWAYS_INLINE uintptr_t currentEnd() const {
JS_ASSERT(runtime_);
JS_ASSERT(currentEnd_ == chunk(currentChunk_).end());

View File

@ -1510,6 +1510,29 @@ CodeGenerator::visitPostWriteBarrierV(LPostWriteBarrierV *lir)
return true;
}
bool
CodeGenerator::visitPostWriteBarrierAllSlots(LPostWriteBarrierAllSlots *lir)
{
#ifdef JSGC_GENERATIONAL
OutOfLineCallPostWriteBarrier *ool = new OutOfLineCallPostWriteBarrier(lir, lir->object());
if (!addOutOfLineCode(ool))
return false;
Nursery &nursery = GetIonContext()->runtime->gcNursery;
if (lir->object()->isConstant()) {
JS_ASSERT(!nursery.isInside(&lir->object()->toConstant()->toObject()));
return true;
}
Register objreg = ToRegister(lir->object());
masm.branchPtr(Assembler::Below, objreg, ImmWord(nursery.start()), ool->entry());
masm.branchPtr(Assembler::Below, objreg, ImmWord(nursery.heapEnd()), ool->rejoin());
masm.bind(ool->rejoin());
#endif
return true;
}
bool
CodeGenerator::visitCallNative(LCallNative *call)
{
@ -3416,7 +3439,8 @@ CodeGenerator::visitCreateThisWithProto(LCreateThisWithProto *lir)
return callVM(CreateThisWithProtoInfo, lir);
}
typedef JSObject *(*NewGCThingFn)(JSContext *cx, gc::AllocKind allocKind, size_t thingSize);
typedef JSObject *(*NewGCThingFn)(JSContext *cx, gc::AllocKind allocKind, size_t thingSize,
gc::InitialHeap initialHeap);
static const VMFunction NewGCThingInfo =
FunctionInfo<NewGCThingFn>(js::jit::NewGCThing);
@ -3426,10 +3450,11 @@ CodeGenerator::visitCreateThisWithTemplate(LCreateThisWithTemplate *lir)
JSObject *templateObject = lir->mir()->getTemplateObject();
gc::AllocKind allocKind = templateObject->tenuredGetAllocKind();
int thingSize = (int)gc::Arena::thingSize(allocKind);
gc::InitialHeap initialHeap = templateObject->type()->initialHeapForJITAlloc();
Register objReg = ToRegister(lir->output());
OutOfLineCode *ool = oolCallVM(NewGCThingInfo, lir,
(ArgList(), Imm32(allocKind), Imm32(thingSize)),
(ArgList(), Imm32(allocKind), Imm32(thingSize), Imm32(initialHeap)),
StoreRegisterTo(objReg));
if (!ool)
return false;

View File

@ -100,6 +100,7 @@ class CodeGenerator : public CodeGeneratorSpecific
bool visitMonitorTypes(LMonitorTypes *lir);
bool visitPostWriteBarrierO(LPostWriteBarrierO *lir);
bool visitPostWriteBarrierV(LPostWriteBarrierV *lir);
bool visitPostWriteBarrierAllSlots(LPostWriteBarrierAllSlots *lir);
bool visitOutOfLineCallPostWriteBarrier(OutOfLineCallPostWriteBarrier *ool);
bool visitCallNative(LCallNative *call);
bool emitCallInvokeFunction(LInstruction *call, Register callereg,

View File

@ -4570,6 +4570,11 @@ IonBuilder::createCallObject(MDefinition *callee, MDefinition *scope)
MInstruction *callObj = MNewCallObject::New(templateObj, script()->treatAsRunOnce, slots);
current->add(callObj);
// Insert a post barrier to protect the following writes if we allocated
// the new call object directly into tenured storage.
if (templateObj->type()->isLongLivedForJITAlloc())
current->add(MPostWriteBarrier::New(callObj));
// Initialize the object's reserved slots. No post barrier is needed here,
// for the same reason as in createDeclEnvObject.
current->add(MStoreFixedSlot::New(callObj, CallObject::enclosingScopeSlot(), scope));

View File

@ -610,7 +610,8 @@ MacroAssembler::clampDoubleToUint8(FloatRegister input, Register output)
}
void
MacroAssembler::newGCThing(const Register &result, gc::AllocKind allocKind, Label *fail)
MacroAssembler::newGCThing(const Register &result, gc::AllocKind allocKind, Label *fail,
gc::InitialHeap initialHeap /* = gc::DefaultHeap */)
{
// Inlined equivalent of js::gc::NewGCThing() without failure case handling.
@ -632,7 +633,10 @@ MacroAssembler::newGCThing(const Register &result, gc::AllocKind allocKind, Labe
#ifdef JSGC_GENERATIONAL
Nursery &nursery = GetIonContext()->runtime->gcNursery;
if (nursery.isEnabled() && allocKind <= gc::FINALIZE_OBJECT_LAST) {
if (nursery.isEnabled() &&
allocKind <= gc::FINALIZE_OBJECT_LAST &&
initialHeap != gc::TenuredHeap)
{
// Inline Nursery::allocate. No explicit check for nursery.isEnabled()
// is needed, as the comparison with the nursery's end will always fail
// in such cases.
@ -666,7 +670,8 @@ MacroAssembler::newGCThing(const Register &result, JSObject *templateObject, Lab
JS_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST);
JS_ASSERT(!templateObject->hasDynamicElements());
newGCThing(result, allocKind, fail);
gc::InitialHeap initialHeap = templateObject->type()->initialHeapForJITAlloc();
newGCThing(result, allocKind, fail, initialHeap);
}
void

View File

@ -762,7 +762,8 @@ class MacroAssembler : public MacroAssemblerSpecific
Label *label);
// Inline allocation.
void newGCThing(const Register &result, gc::AllocKind allocKind, Label *fail);
void newGCThing(const Register &result, gc::AllocKind allocKind, Label *fail,
gc::InitialHeap initialHeap = gc::DefaultHeap);
void newGCThing(const Register &result, JSObject *templateObject, Label *fail);
void newGCString(const Register &result, Label *fail);
void newGCShortString(const Register &result, Label *fail);

View File

@ -4680,6 +4680,24 @@ class LPostWriteBarrierV : public LInstructionHelper<0, 1 + BOX_PIECES, 1>
}
};
// Generational write barrier used when writing to multiple slots in an object.
class LPostWriteBarrierAllSlots : public LInstructionHelper<0, 1, 0>
{
public:
LIR_HEADER(PostWriteBarrierAllSlots)
LPostWriteBarrierAllSlots(const LAllocation &obj) {
setOperand(0, obj);
}
const MPostWriteBarrier *mir() const {
return mir_->toPostWriteBarrier();
}
const LAllocation *object() {
return getOperand(0);
}
};
// Guard against an object's class.
class LGuardClass : public LInstructionHelper<0, 1, 1>
{

View File

@ -156,6 +156,7 @@
_(MonitorTypes) \
_(PostWriteBarrierO) \
_(PostWriteBarrierV) \
_(PostWriteBarrierAllSlots) \
_(InitializedLength) \
_(SetInitializedLength) \
_(BoundsCheck) \

View File

@ -2017,6 +2017,11 @@ bool
LIRGenerator::visitPostWriteBarrier(MPostWriteBarrier *ins)
{
#ifdef JSGC_GENERATIONAL
if (!ins->hasValue()) {
LPostWriteBarrierAllSlots *lir =
new LPostWriteBarrierAllSlots(useRegisterOrConstant(ins->object()));
return add(lir, ins) && assignSafepoint(lir, ins);
}
switch (ins->value()->type()) {
case MIRType_Object: {
LPostWriteBarrierO *lir = new LPostWriteBarrierO(useRegisterOrConstant(ins->object()),

View File

@ -7869,8 +7869,16 @@ class MPostWriteBarrier
: public MBinaryInstruction,
public ObjectPolicy<0>
{
bool hasValue_;
MPostWriteBarrier(MDefinition *obj, MDefinition *value)
: MBinaryInstruction(obj, value)
: MBinaryInstruction(obj, value), hasValue_(true)
{
setGuard();
}
MPostWriteBarrier(MDefinition *obj)
: MBinaryInstruction(obj, NULL), hasValue_(false)
{
setGuard();
}
@ -7882,6 +7890,10 @@ class MPostWriteBarrier
return new MPostWriteBarrier(obj, value);
}
static MPostWriteBarrier *New(MDefinition *obj) {
return new MPostWriteBarrier(obj);
}
TypePolicy *typePolicy() {
return this;
}
@ -7889,6 +7901,10 @@ class MPostWriteBarrier
MDefinition *object() const {
return getOperand(0);
}
bool hasValue() const {
return hasValue_;
}
MDefinition *value() const {
return getOperand(1);
}

View File

@ -94,9 +94,9 @@ InvokeFunction(JSContext *cx, HandleObject obj0, uint32_t argc, Value *argv, Val
}
JSObject *
NewGCThing(JSContext *cx, gc::AllocKind allocKind, size_t thingSize)
NewGCThing(JSContext *cx, gc::AllocKind allocKind, size_t thingSize, gc::InitialHeap initialHeap)
{
return gc::NewGCThing<JSObject, CanGC>(cx, allocKind, thingSize, gc::DefaultHeap);
return gc::NewGCThing<JSObject, CanGC>(cx, allocKind, thingSize, initialHeap);
}
bool
@ -269,6 +269,8 @@ NewInitArray(JSContext *cx, uint32_t count, types::TypeObject *typeArg)
{
RootedTypeObject type(cx, typeArg);
NewObjectKind newKind = !type ? SingletonObject : GenericObject;
if (type && type->isLongLivedForJITAlloc())
newKind = TenuredObject;
RootedObject obj(cx, NewDenseAllocatedArray(cx, count, NULL, newKind));
if (!obj)
return NULL;
@ -285,6 +287,8 @@ JSObject*
NewInitObject(JSContext *cx, HandleObject templateObject)
{
NewObjectKind newKind = templateObject->hasSingletonType() ? SingletonObject : GenericObject;
if (!templateObject->hasLazyType() && templateObject->type()->isLongLivedForJITAlloc())
newKind = TenuredObject;
RootedObject obj(cx, CopyInitializerObject(cx, templateObject, newKind));
if (!obj)
@ -302,11 +306,16 @@ JSObject *
NewInitObjectWithClassPrototype(JSContext *cx, HandleObject templateObject)
{
JS_ASSERT(!templateObject->hasSingletonType());
JS_ASSERT(!templateObject->hasLazyType());
NewObjectKind newKind = templateObject->type()->isLongLivedForJITAlloc()
? TenuredObject
: GenericObject;
JSObject *obj = NewObjectWithGivenProto(cx,
templateObject->getClass(),
templateObject->getProto(),
cx->global());
cx->global(),
newKind);
if (!obj)
return NULL;
@ -751,7 +760,10 @@ InitRestParameter(JSContext *cx, uint32_t length, Value *rest, HandleObject temp
return arrRes;
}
ArrayObject *arrRes = NewDenseCopiedArray(cx, length, rest, NULL);
NewObjectKind newKind = templateObj->type()->isLongLivedForJITAlloc()
? TenuredObject
: GenericObject;
ArrayObject *arrRes = NewDenseCopiedArray(cx, length, rest, NULL, newKind);
if (arrRes)
arrRes->setType(templateObj->type());
return arrRes;

View File

@ -575,7 +575,8 @@ class AutoDetectInvalidation
};
bool InvokeFunction(JSContext *cx, HandleObject obj0, uint32_t argc, Value *argv, Value *rval);
JSObject *NewGCThing(JSContext *cx, gc::AllocKind allocKind, size_t thingSize);
JSObject *NewGCThing(JSContext *cx, gc::AllocKind allocKind, size_t thingSize,
gc::InitialHeap initialHeap);
bool CheckOverRecursed(JSContext *cx);
bool CheckOverRecursedWithExtra(JSContext *cx, uint32_t extra);

View File

@ -865,6 +865,21 @@ class TypeConstraintFreezeOwnProperty : public TypeConstraint
static void
CheckNewScriptProperties(JSContext *cx, HandleTypeObject type, HandleFunction fun);
bool
TypeObject::incrementTenureCount()
{
uint32_t count = tenureCount();
JS_ASSERT(count <= OBJECT_FLAG_TENURE_COUNT_LIMIT);
if (count >= OBJECT_FLAG_TENURE_COUNT_LIMIT)
return false;
flags = (flags & ~OBJECT_FLAG_TENURE_COUNT_MASK)
| ((count + 1) << OBJECT_FLAG_TENURE_COUNT_SHIFT);
return count >= MaxJITAllocTenures;
}
bool
HeapTypeSet::isOwnProperty(JSContext *cx, TypeObject *object, bool configurable)
{

View File

@ -409,6 +409,12 @@ enum {
/* Flags which indicate dynamic properties of represented objects. */
OBJECT_FLAG_DYNAMIC_MASK = 0x00ff0000,
/* Mask/shift for tenuring count. */
OBJECT_FLAG_TENURE_COUNT_MASK = 0x7f000000,
OBJECT_FLAG_TENURE_COUNT_SHIFT = 24,
OBJECT_FLAG_TENURE_COUNT_LIMIT =
OBJECT_FLAG_TENURE_COUNT_MASK >> OBJECT_FLAG_TENURE_COUNT_SHIFT,
/*
* Whether all properties of this object are considered unknown.
* If set, all flags in DYNAMIC_MASK will also be set.
@ -1115,6 +1121,39 @@ struct TypeObject : gc::BarrieredCell<TypeObject>
*/
//inline JSObject *getGlobal();
/* Tenure counter management. */
/*
* When an object allocation site generates objects that are long lived
* enough to frequently be tenured during minor collections, we mark the
* site as long lived and allocate directly into the tenured generation.
*/
const static uint32_t MaxJITAllocTenures = OBJECT_FLAG_TENURE_COUNT_LIMIT - 2;
/*
* NewObjectCache is used when we take a stub for allocation. It is used
* more rarely, but still in hot paths, so pre-tenure with fewer uses.
*/
const static uint32_t MaxCachedAllocTenures = 64;
/* Returns true if the allocating script should be recompiled. */
bool incrementTenureCount();
uint32_t tenureCount() const {
return (flags & OBJECT_FLAG_TENURE_COUNT_MASK) >> OBJECT_FLAG_TENURE_COUNT_SHIFT;
}
bool isLongLivedForCachedAlloc() const {
return tenureCount() >= MaxCachedAllocTenures;
}
bool isLongLivedForJITAlloc() const {
return tenureCount() >= MaxJITAllocTenures;
}
gc::InitialHeap initialHeapForJITAlloc() const {
return isLongLivedForJITAlloc() ? gc::TenuredHeap : gc::DefaultHeap;
}
/* Helpers */
bool addProperty(ExclusiveContext *cx, jsid id, Property **pprop);

View File

@ -48,9 +48,13 @@ NewObjectCache::newObjectFromHit(JSContext *cx, EntryIndex entry_, js::gc::Initi
JS_ASSERT(unsigned(entry_) < mozilla::ArrayLength(entries));
Entry *entry = &entries[entry_];
JSObject *templateObj = reinterpret_cast<JSObject *>(&entry->templateObject);
if (templateObj->type()->isLongLivedForCachedAlloc())
heap = gc::TenuredHeap;
JSObject *obj = js_NewGCObject<NoGC>(cx, entry->kind, heap);
if (obj) {
copyCachedToObject(obj, reinterpret_cast<JSObject *>(&entry->templateObject), entry->kind);
copyCachedToObject(obj, templateObj, entry->kind);
Probes::createObject(cx, obj);
return obj;
}