mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 889129 - Fix performance on Splay with generational GC enabled; r=bhackett
--HG-- extra : rebase_source : bebc97112c6d7e09d42d7083e49b7bb6cd6dceae
This commit is contained in:
parent
ac4d65e1fd
commit
bf843e6c76
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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));
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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>
|
||||
{
|
||||
|
@ -156,6 +156,7 @@
|
||||
_(MonitorTypes) \
|
||||
_(PostWriteBarrierO) \
|
||||
_(PostWriteBarrierV) \
|
||||
_(PostWriteBarrierAllSlots) \
|
||||
_(InitializedLength) \
|
||||
_(SetInitializedLength) \
|
||||
_(BoundsCheck) \
|
||||
|
@ -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()),
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user