Bug 1157231 - Optimize calls to own property setters. r=efaust

This commit is contained in:
Jan de Mooij 2015-05-08 21:41:50 +02:00
parent a7aa64cbed
commit 2e4d61a916
3 changed files with 84 additions and 59 deletions

View File

@ -493,7 +493,7 @@ ICStub::trace(JSTracer* trc)
}
case ICStub::SetProp_CallScripted: {
ICSetProp_CallScripted* callStub = toSetProp_CallScripted();
callStub->guard().trace(trc);
callStub->receiverGuard().trace(trc);
TraceEdge(trc, &callStub->holder(), "baseline-setpropcallscripted-stub-holder");
TraceEdge(trc, &callStub->holderShape(), "baseline-setpropcallscripted-stub-holdershape");
TraceEdge(trc, &callStub->setter(), "baseline-setpropcallscripted-stub-setter");
@ -501,7 +501,7 @@ ICStub::trace(JSTracer* trc)
}
case ICStub::SetProp_CallNative: {
ICSetProp_CallNative* callStub = toSetProp_CallNative();
callStub->guard().trace(trc);
callStub->receiverGuard().trace(trc);
TraceEdge(trc, &callStub->holder(), "baseline-setpropcallnative-stub-holder");
TraceEdge(trc, &callStub->holderShape(), "baseline-setpropcallnative-stub-holdershape");
TraceEdge(trc, &callStub->setter(), "baseline-setpropcallnative-stub-setter");
@ -3580,10 +3580,6 @@ IsCacheableSetPropCall(JSContext* cx, JSObject* obj, JSObject* holder, Shape* sh
{
MOZ_ASSERT(isScripted);
// Currently we only optimize setter calls for setters bound on prototypes.
if (obj == holder)
return false;
if (!shape || !IsCacheableProtoChain(obj, holder))
return false;
@ -6414,26 +6410,40 @@ static bool
UpdateExistingSetPropCallStubs(ICSetProp_Fallback* fallbackStub,
ICStub::Kind kind,
NativeObject* holder,
ReceiverGuard receiverGuard,
JSObject* receiver,
JSFunction* setter)
{
MOZ_ASSERT(kind == ICStub::SetProp_CallScripted ||
kind == ICStub::SetProp_CallNative);
MOZ_ASSERT(holder);
MOZ_ASSERT(receiver);
bool isOwnSetter = (holder == receiver);
bool foundMatchingStub = false;
ReceiverGuard receiverGuard(receiver);
for (ICStubConstIterator iter = fallbackStub->beginChainConst(); !iter.atEnd(); iter++) {
if (iter->kind() == kind) {
ICSetPropCallSetter* setPropStub = static_cast<ICSetPropCallSetter*>(*iter);
if (setPropStub->holder() == holder) {
if (setPropStub->holder() == holder && setPropStub->isOwnSetter() == isOwnSetter) {
// If this is an own setter, update the receiver guard as well,
// since that's the shape we'll be guarding on. Furthermore,
// isOwnSetter() relies on holderShape_ and receiverGuard_ being
// the same shape.
if (isOwnSetter)
setPropStub->receiverGuard().update(receiverGuard);
MOZ_ASSERT(setPropStub->holderShape() != holder->lastProperty() ||
!setPropStub->receiverGuard().matches(receiverGuard),
"Why didn't we end up using this stub?");
// We want to update the holder shape to match the new one no
// matter what, even if the receiver shape is different.
MOZ_ASSERT(setPropStub->holderShape() != holder->lastProperty() ||
!setPropStub->guard().matches(receiverGuard),
"Why didn't we end up using this stub?");
setPropStub->holderShape() = holder->lastProperty();
// Make sure to update the setter, since a shape change might
// have changed which setter we want to use.
setPropStub->setter() = setter;
if (setPropStub->guard().matches(receiverGuard))
if (setPropStub->receiverGuard().matches(receiverGuard))
foundMatchingStub = true;
}
}
@ -8882,11 +8892,10 @@ TryAttachSetAccessorPropStub(JSContext* cx, HandleScript script, jsbytecode* pc,
// Try handling scripted setters.
if (cacheableCall && isScripted) {
RootedFunction callee(cx, &shape->setterObject()->as<JSFunction>());
MOZ_ASSERT(obj != holder);
MOZ_ASSERT(callee->hasScript());
if (UpdateExistingSetPropCallStubs(stub, ICStub::SetProp_CallScripted,
&holder->as<NativeObject>(), receiverGuard, callee)) {
&holder->as<NativeObject>(), obj, callee)) {
*attached = true;
return true;
}
@ -8907,11 +8916,10 @@ TryAttachSetAccessorPropStub(JSContext* cx, HandleScript script, jsbytecode* pc,
// Try handling JSNative setters.
if (cacheableCall && !isScripted) {
RootedFunction callee(cx, &shape->setterObject()->as<JSFunction>());
MOZ_ASSERT(obj != holder);
MOZ_ASSERT(callee->isNative());
if (UpdateExistingSetPropCallStubs(stub, ICStub::SetProp_CallNative,
&holder->as<NativeObject>(), receiverGuard, callee)) {
&holder->as<NativeObject>(), obj, callee)) {
*attached = true;
return true;
}
@ -9651,14 +9659,16 @@ ICSetProp_CallScripted::Compiler::generateStubCode(MacroAssembler& masm)
// Unbox and shape guard.
Register objReg = masm.extractObject(R0, ExtractTemp0);
GuardReceiverObject(masm, ReceiverGuard(obj_), objReg, scratch,
ICSetProp_CallScripted::offsetOfGuard(), &failureUnstow);
GuardReceiverObject(masm, ReceiverGuard(receiver_), objReg, scratch,
ICSetProp_CallScripted::offsetOfReceiverGuard(), &failureUnstow);
Register holderReg = regs.takeAny();
masm.loadPtr(Address(BaselineStubReg, ICSetProp_CallScripted::offsetOfHolder()), holderReg);
masm.loadPtr(Address(BaselineStubReg, ICSetProp_CallScripted::offsetOfHolderShape()), scratch);
masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratch, &failureUnstow);
regs.add(holderReg);
if (receiver_ != holder_) {
Register holderReg = regs.takeAny();
masm.loadPtr(Address(BaselineStubReg, ICSetProp_CallScripted::offsetOfHolder()), holderReg);
masm.loadPtr(Address(BaselineStubReg, ICSetProp_CallScripted::offsetOfHolderShape()), scratch);
masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratch, &failureUnstow);
regs.add(holderReg);
}
// Push a stub frame so that we can perform a non-tail call.
enterStubFrame(masm, scratch);
@ -9770,14 +9780,16 @@ ICSetProp_CallNative::Compiler::generateStubCode(MacroAssembler& masm)
// Unbox and shape guard.
Register objReg = masm.extractObject(R0, ExtractTemp0);
GuardReceiverObject(masm, ReceiverGuard(obj_), objReg, scratch,
ICSetProp_CallNative::offsetOfGuard(), &failureUnstow);
GuardReceiverObject(masm, ReceiverGuard(receiver_), objReg, scratch,
ICSetProp_CallNative::offsetOfReceiverGuard(), &failureUnstow);
Register holderReg = regs.takeAny();
masm.loadPtr(Address(BaselineStubReg, ICSetProp_CallNative::offsetOfHolder()), holderReg);
masm.loadPtr(Address(BaselineStubReg, ICSetProp_CallNative::offsetOfHolderShape()), scratch);
masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratch, &failureUnstow);
regs.add(holderReg);
if (receiver_ != holder_) {
Register holderReg = regs.takeAny();
masm.loadPtr(Address(BaselineStubReg, ICSetProp_CallNative::offsetOfHolder()), holderReg);
masm.loadPtr(Address(BaselineStubReg, ICSetProp_CallNative::offsetOfHolderShape()), scratch);
masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratch, &failureUnstow);
regs.add(holderReg);
}
// Push a stub frame so that we can perform a non-tail call.
enterStubFrame(masm, scratch);
@ -12877,11 +12889,11 @@ ICSetPropNativeAddCompiler::ICSetPropNativeAddCompiler(JSContext* cx, HandleObje
MOZ_ASSERT(protoChainDepth_ <= ICSetProp_NativeAdd::MAX_PROTO_CHAIN_DEPTH);
}
ICSetPropCallSetter::ICSetPropCallSetter(Kind kind, JitCode* stubCode, ReceiverGuard guard,
ICSetPropCallSetter::ICSetPropCallSetter(Kind kind, JitCode* stubCode, ReceiverGuard receiverGuard,
JSObject* holder, Shape* holderShape,
JSFunction* setter, uint32_t pcOffset)
: ICStub(kind, stubCode),
guard_(guard),
receiverGuard_(receiverGuard),
holder_(holder),
holderShape_(holderShape),
setter_(setter),
@ -12894,15 +12906,17 @@ ICSetPropCallSetter::ICSetPropCallSetter(Kind kind, JitCode* stubCode, ReceiverG
ICSetProp_CallScripted::Clone(JSContext* cx, ICStubSpace* space, ICStub*,
ICSetProp_CallScripted& other)
{
return New<ICSetProp_CallScripted>(cx, space, other.jitCode(), other.guard(), other.holder_,
other.holderShape_, other.setter_, other.pcOffset_);
return New<ICSetProp_CallScripted>(cx, space, other.jitCode(), other.receiverGuard(),
other.holder_, other.holderShape_, other.setter_,
other.pcOffset_);
}
/* static */ ICSetProp_CallNative*
ICSetProp_CallNative::Clone(JSContext* cx, ICStubSpace* space, ICStub*, ICSetProp_CallNative& other)
{
return New<ICSetProp_CallNative>(cx, space, other.jitCode(), other.guard(), other.holder_,
other.holderShape_, other.setter_, other.pcOffset_);
return New<ICSetProp_CallNative>(cx, space, other.jitCode(), other.receiverGuard(),
other.holder_, other.holderShape_, other.setter_,
other.pcOffset_);
}
ICCall_Scripted::ICCall_Scripted(JitCode* stubCode, ICStub* firstMonitorStub,

View File

@ -4574,6 +4574,12 @@ class ICGetPropCallGetter : public ICMonitoredStub
return receiverGuard_;
}
bool isOwnGetter() const {
MOZ_ASSERT(holder_->isNative());
MOZ_ASSERT(holderShape_);
return receiverGuard_.shape() == holderShape_;
}
static size_t offsetOfHolder() {
return offsetof(ICGetPropCallGetter, holder_);
}
@ -4590,12 +4596,6 @@ class ICGetPropCallGetter : public ICMonitoredStub
return offsetof(ICGetPropCallGetter, receiverGuard_);
}
bool isOwnGetter() const {
MOZ_ASSERT(holder_->isNative());
MOZ_ASSERT(holderShape_);
return receiverGuard_.shape() == holderShape_;
}
class Compiler : public ICStubCompiler {
protected:
ICStub* firstMonitorStub_;
@ -5331,10 +5331,13 @@ class ICSetPropCallSetter : public ICStub
friend class ICStubSpace;
protected:
// Object shape/group.
HeapReceiverGuard guard_;
// Shape/group of receiver object. Used for both own and proto setters.
HeapReceiverGuard receiverGuard_;
// Holder and shape.
// Holder and holder shape. For own setters, guarding on receiverGuard_ is
// sufficient, although Ion may use holder_ and holderShape_ even for own
// setters. In this case holderShape_ == receiverGuard_.shape_ (isOwnSetter
// below relies on this).
HeapPtrObject holder_;
HeapPtrShape holderShape_;
@ -5344,13 +5347,13 @@ class ICSetPropCallSetter : public ICStub
// PC of call, for profiler
uint32_t pcOffset_;
ICSetPropCallSetter(Kind kind, JitCode* stubCode, ReceiverGuard guard,
ICSetPropCallSetter(Kind kind, JitCode* stubCode, ReceiverGuard receiverGuard,
JSObject* holder, Shape* holderShape, JSFunction* setter,
uint32_t pcOffset);
public:
HeapReceiverGuard& guard() {
return guard_;
HeapReceiverGuard& receiverGuard() {
return receiverGuard_;
}
HeapPtrObject& holder() {
return holder_;
@ -5362,8 +5365,14 @@ class ICSetPropCallSetter : public ICStub
return setter_;
}
static size_t offsetOfGuard() {
return offsetof(ICSetPropCallSetter, guard_);
bool isOwnSetter() const {
MOZ_ASSERT(holder_->isNative());
MOZ_ASSERT(holderShape_);
return receiverGuard_.shape() == holderShape_;
}
static size_t offsetOfReceiverGuard() {
return offsetof(ICSetPropCallSetter, receiverGuard_);
}
static size_t offsetOfHolder() {
return offsetof(ICSetPropCallSetter, holder_);
@ -5380,21 +5389,22 @@ class ICSetPropCallSetter : public ICStub
class Compiler : public ICStubCompiler {
protected:
RootedObject obj_;
RootedObject receiver_;
RootedObject holder_;
RootedFunction setter_;
uint32_t pcOffset_;
virtual int32_t getKey() const {
return static_cast<int32_t>(kind) |
(HeapReceiverGuard::keyBits(obj_) << 16);
(HeapReceiverGuard::keyBits(receiver_) << 16) |
(static_cast<int32_t>(receiver_ != holder_) << 19);
}
public:
Compiler(JSContext* cx, ICStub::Kind kind, HandleObject obj, HandleObject holder,
Compiler(JSContext* cx, ICStub::Kind kind, HandleObject receiver, HandleObject holder,
HandleFunction setter, uint32_t pcOffset)
: ICStubCompiler(cx, kind),
obj_(cx, obj),
receiver_(cx, receiver),
holder_(cx, holder),
setter_(cx, setter),
pcOffset_(pcOffset)
@ -5432,7 +5442,7 @@ class ICSetProp_CallScripted : public ICSetPropCallSetter
{}
ICStub* getStub(ICStubSpace* space) {
ReceiverGuard guard(obj_);
ReceiverGuard guard(receiver_);
Shape* holderShape = holder_->as<NativeObject>().lastProperty();
return newStub<ICSetProp_CallScripted>(space, getStubCode(), guard, holder_,
holderShape, setter_, pcOffset_);
@ -5469,7 +5479,7 @@ class ICSetProp_CallNative : public ICSetPropCallSetter
{}
ICStub* getStub(ICStubSpace* space) {
ReceiverGuard guard(obj_);
ReceiverGuard guard(receiver_);
Shape* holderShape = holder_->as<NativeObject>().lastProperty();
return newStub<ICSetProp_CallNative>(space, getStubCode(), guard, holder_, holderShape,
setter_, pcOffset_);

View File

@ -672,15 +672,16 @@ BaselineInspector::commonSetPropFunction(jsbytecode* pc, JSObject** holder, Shap
for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) {
if (stub->isSetProp_CallScripted() || stub->isSetProp_CallNative()) {
ICSetPropCallSetter* nstub = static_cast<ICSetPropCallSetter*>(stub);
if (!AddReceiver(nstub->guard(), receivers, convertUnboxedGroups))
bool isOwn = nstub->isOwnSetter();
if (!isOwn && !AddReceiver(nstub->receiverGuard(), receivers, convertUnboxedGroups))
return false;
if (!*holder) {
*holder = nstub->holder();
*holderShape = nstub->holderShape();
*commonSetter = nstub->setter();
*isOwnProperty = false;
} else if (nstub->holderShape() != *holderShape) {
*isOwnProperty = isOwn;
} else if (nstub->holderShape() != *holderShape || isOwn != *isOwnProperty) {
return false;
} else {
MOZ_ASSERT(*commonSetter == nstub->setter());