Bug 1157231 - Optimize calls to own property setters. r=efaust
authorJan de Mooij <jdemooij@mozilla.com>
Fri, 08 May 2015 21:41:50 +0200
changeset 274424 723039c4f5142fe74e1e0cbdd976d291c0345134
parent 274423 8b2364aef0dd5912953cc4bbf43d7eaf2c9fd358
child 274425 cab1a4754903ad65b7283c03ecac005b27bdd88f
push id863
push userraliiev@mozilla.com
push dateMon, 03 Aug 2015 13:22:43 +0000
treeherdermozilla-release@f6321b14228d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersefaust
bugs1157231
milestone40.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1157231 - Optimize calls to own property setters. r=efaust
js/src/jit/BaselineIC.cpp
js/src/jit/BaselineIC.h
js/src/jit/BaselineInspector.cpp
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -488,25 +488,25 @@ ICStub::trace(JSTracer* trc)
       case ICStub::SetProp_TypedObject: {
         ICSetProp_TypedObject* propStub = toSetProp_TypedObject();
         TraceEdge(trc, &propStub->shape(), "baseline-setprop-typedobject-stub-shape");
         TraceEdge(trc, &propStub->group(), "baseline-setprop-typedobject-stub-group");
         break;
       }
       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");
         break;
       }
       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");
         break;
       }
       case ICStub::InstanceOf_Function: {
         ICInstanceOf_Function* instanceofStub = toInstanceOf_Function();
         TraceEdge(trc, &instanceofStub->shape(), "baseline-instanceof-fun-shape");
@@ -3575,20 +3575,16 @@ IsCacheableSetPropAddSlot(JSContext* cx,
 }
 
 static bool
 IsCacheableSetPropCall(JSContext* cx, JSObject* obj, JSObject* holder, Shape* shape,
                        bool* isScripted, bool* isTemporarilyUnoptimizable)
 {
     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;
 
     if (shape->hasSlot() || shape->hasDefaultSetter())
         return false;
 
     if (!shape->hasSetterValue())
         return false;
@@ -6409,36 +6405,50 @@ UpdateExistingGetPropCallStubs(ICFallbac
 }
 
 // Try to update existing SetProp setter call stubs for the given holder in
 // place with a new shape and setter.
 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;
             }
         }
     }
 
     return foundMatchingStub;
 }
 
@@ -8877,21 +8887,20 @@ TryAttachSetAccessorPropStub(JSContext* 
 
     bool isScripted = false;
     bool cacheableCall = IsCacheableSetPropCall(cx, obj, holder, shape,
                                                 &isScripted, isTemporarilyUnoptimizable);
 
     // 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;
         }
 
         JitSpew(JitSpew_BaselineIC, "  Generating SetProp(NativeObj/ScriptedSetter %s:%" PRIuSIZE ") stub",
                     callee->nonLazyScript()->filename(), callee->nonLazyScript()->lineno());
 
         ICSetProp_CallScripted::Compiler compiler(cx, obj, holder, callee, script->pcToOffset(pc));
@@ -8902,21 +8911,20 @@ TryAttachSetAccessorPropStub(JSContext* 
         stub->addNewStub(newStub);
         *attached = true;
         return true;
     }
 
     // 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;
         }
 
         JitSpew(JitSpew_BaselineIC, "  Generating SetProp(NativeObj/NativeSetter %p) stub",
                     callee->native());
 
         ICSetProp_CallNative::Compiler compiler(cx, obj, holder, callee, script->pcToOffset(pc));
@@ -9646,24 +9654,26 @@ ICSetProp_CallScripted::Compiler::genera
     // Stow R0 and R1 to free up registers.
     EmitStowICValues(masm, 2);
 
     AllocatableGeneralRegisterSet regs(availableGeneralRegs(1));
     Register scratch = regs.takeAnyExcluding(BaselineTailCallReg);
 
     // Unbox and shape guard.
     Register objReg = masm.extractObject(R0, ExtractTemp0);
-    GuardReceiverObject(masm, ReceiverGuard(obj_), objReg, scratch,
-                        ICSetProp_CallScripted::offsetOfGuard(), &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);
+    GuardReceiverObject(masm, ReceiverGuard(receiver_), objReg, scratch,
+                        ICSetProp_CallScripted::offsetOfReceiverGuard(), &failureUnstow);
+
+    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);
 
     // Load callee function and code.  To ensure that |code| doesn't end up being
     // ArgumentsRectifierReg, if it's available we assign it to |callee| instead.
     Register callee;
     if (regs.has(ArgumentsRectifierReg)) {
@@ -9765,24 +9775,26 @@ ICSetProp_CallNative::Compiler::generate
     // Stow R0 and R1 to free up registers.
     EmitStowICValues(masm, 2);
 
     AllocatableGeneralRegisterSet regs(availableGeneralRegs(1));
     Register scratch = regs.takeAnyExcluding(BaselineTailCallReg);
 
     // Unbox and shape guard.
     Register objReg = masm.extractObject(R0, ExtractTemp0);
-    GuardReceiverObject(masm, ReceiverGuard(obj_), objReg, scratch,
-                        ICSetProp_CallNative::offsetOfGuard(), &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);
+    GuardReceiverObject(masm, ReceiverGuard(receiver_), objReg, scratch,
+                        ICSetProp_CallNative::offsetOfReceiverGuard(), &failureUnstow);
+
+    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);
 
     // Load callee function and code.  To ensure that |code| doesn't end up being
     // ArgumentsRectifierReg, if it's available we assign it to |callee| instead.
     Register callee = regs.takeAny();
     masm.loadPtr(Address(BaselineStubReg, ICSetProp_CallNative::offsetOfSetter()), callee);
@@ -12872,42 +12884,44 @@ ICSetPropNativeAddCompiler::ICSetPropNat
     oldGroup_(cx, oldGroup),
     protoChainDepth_(protoChainDepth),
     isFixedSlot_(isFixedSlot),
     offset_(offset)
 {
     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),
     pcOffset_(pcOffset)
 {
     MOZ_ASSERT(kind == ICStub::SetProp_CallScripted || kind == ICStub::SetProp_CallNative);
 }
 
 /* static */ ICSetProp_CallScripted*
 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,
                                  JSFunction* callee, JSObject* templateObject,
                                  uint32_t pcOffset)
   : ICMonitoredStub(ICStub::Call_Scripted, stubCode, firstMonitorStub),
     callee_(callee),
     templateObject_(templateObject),
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -4569,38 +4569,38 @@ class ICGetPropCallGetter : public ICMon
     }
     HeapPtrFunction& getter() {
         return getter_;
     }
     HeapReceiverGuard& receiverGuard() {
         return receiverGuard_;
     }
 
+    bool isOwnGetter() const {
+        MOZ_ASSERT(holder_->isNative());
+        MOZ_ASSERT(holderShape_);
+        return receiverGuard_.shape() == holderShape_;
+    }
+
     static size_t offsetOfHolder() {
         return offsetof(ICGetPropCallGetter, holder_);
     }
     static size_t offsetOfHolderShape() {
         return offsetof(ICGetPropCallGetter, holderShape_);
     }
     static size_t offsetOfGetter() {
         return offsetof(ICGetPropCallGetter, getter_);
     }
     static size_t offsetOfPCOffset() {
         return offsetof(ICGetPropCallGetter, pcOffset_);
     }
     static size_t offsetOfReceiverGuard() {
         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_;
         RootedObject receiver_;
         RootedObject holder_;
         RootedFunction getter_;
         uint32_t pcOffset_;
         const Class* outerClass_;
@@ -5326,80 +5326,90 @@ class ICSetProp_TypedObject : public ICU
 };
 
 // Base stub for calling a setters on a native or unboxed object.
 class ICSetPropCallSetter : public ICStub
 {
     friend class ICStubSpace;
 
   protected:
-    // Object shape/group.
-    HeapReceiverGuard guard_;
-
-    // Holder and shape.
+    // Shape/group of receiver object. Used for both own and proto setters.
+    HeapReceiverGuard receiverGuard_;
+
+    // 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_;
 
     // Function to call.
     HeapPtrFunction setter_;
 
     // 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_;
     }
     HeapPtrShape& holderShape() {
         return holderShape_;
     }
     HeapPtrFunction& setter() {
         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_);
     }
     static size_t offsetOfHolderShape() {
         return offsetof(ICSetPropCallSetter, holderShape_);
     }
     static size_t offsetOfSetter() {
         return offsetof(ICSetPropCallSetter, setter_);
     }
     static size_t offsetOfPCOffset() {
         return offsetof(ICSetPropCallSetter, pcOffset_);
     }
 
     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)
         {
             MOZ_ASSERT(kind == ICStub::SetProp_CallScripted || kind == ICStub::SetProp_CallNative);
         }
     };
 };
@@ -5427,17 +5437,17 @@ class ICSetProp_CallScripted : public IC
       public:
         Compiler(JSContext* cx, HandleObject obj, HandleObject holder, HandleFunction setter,
                  uint32_t pcOffset)
           : ICSetPropCallSetter::Compiler(cx, ICStub::SetProp_CallScripted,
                                           obj, holder, setter, pcOffset)
         {}
 
         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_);
         }
     };
 };
 
 // Stub for calling a native setter on a native object.
@@ -5464,17 +5474,17 @@ class ICSetProp_CallNative : public ICSe
       public:
         Compiler(JSContext* cx, HandleObject obj, HandleObject holder, HandleFunction setter,
                  uint32_t pcOffset)
           : ICSetPropCallSetter::Compiler(cx, ICStub::SetProp_CallNative,
                                           obj, holder, setter, pcOffset)
         {}
 
         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_);
         }
     };
 };
 
 // Call
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -667,25 +667,26 @@ BaselineInspector::commonSetPropFunction
     MOZ_ASSERT(convertUnboxedGroups.empty());
 
     *holder = nullptr;
     const ICEntry& entry = icEntryFromPC(pc);
 
     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());
             }
         } else if (!stub->isSetProp_Fallback() ||
                    stub->toSetProp_Fallback()->hadUnoptimizableAccess())
         {
             // We have an unoptimizable access, so don't try to optimize.