Bug 1214126 part 1 - Add extra 'id' operand to SetProperty IC. r=efaust
authorJan de Mooij <jdemooij@mozilla.com>
Mon, 26 Oct 2015 11:10:19 +0100
changeset 302881 3bfb54d60c859c73a3c1862d7cec519a60614aff
parent 302880 51036998fbc8f650a2e8d6800b53ed58ab7887ad
child 302882 23218bc0843cd8b239d38fc27c90c49b5ff00844
push id5392
push userraliiev@mozilla.com
push dateMon, 14 Dec 2015 20:08:23 +0000
treeherdermozilla-beta@16ce8562a975 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersefaust
bugs1214126
milestone44.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 1214126 part 1 - Add extra 'id' operand to SetProperty IC. r=efaust
js/src/jit/CodeGenerator.cpp
js/src/jit/CodeGenerator.h
js/src/jit/IonBuilder.cpp
js/src/jit/IonCaches.cpp
js/src/jit/IonCaches.h
js/src/jit/Lowering.cpp
js/src/jit/MIR.h
js/src/jit/TypePolicy.cpp
js/src/jit/shared/LIR-shared.h
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -8475,20 +8475,20 @@ CodeGenerator::addGetPropertyCache(LInst
 {
     GetPropertyIC cache(liveRegs, objReg, id, output, monitoredResult, allowDoubleResult);
     cache.setProfilerLeavePC(profilerLeavePc);
     addCache(ins, allocateCache(cache));
 }
 
 void
 CodeGenerator::addSetPropertyCache(LInstruction* ins, LiveRegisterSet liveRegs, Register objReg,
-                                   Register tempReg, PropertyName* name, ConstantOrRegister value,
+                                   Register tempReg, ConstantOrRegister id, ConstantOrRegister value,
                                    bool strict, bool needsTypeBarrier, jsbytecode* profilerLeavePc)
 {
-    SetPropertyIC cache(liveRegs, objReg, tempReg, name, value, strict, needsTypeBarrier);
+    SetPropertyIC cache(liveRegs, objReg, tempReg, id, value, strict, needsTypeBarrier);
     cache.setProfilerLeavePC(profilerLeavePc);
     addCache(ins, allocateCache(cache));
 }
 
 void
 CodeGenerator::addSetElementCache(LInstruction* ins, Register obj, Register unboxIndex,
                                   Register temp, FloatRegister tempDouble,
                                   FloatRegister tempFloat32, ValueOperand index,
@@ -8715,34 +8715,38 @@ CodeGenerator::visitCallDeleteElement(LC
 }
 
 void
 CodeGenerator::visitSetPropertyCache(LSetPropertyCache* ins)
 {
     LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
     Register objReg = ToRegister(ins->getOperand(0));
     Register tempReg = ToRegister(ins->getTemp(0));
+    ConstantOrRegister id =
+        toConstantOrRegister(ins, LSetPropertyCache::Id, ins->mir()->idval()->type());
     ConstantOrRegister value =
         toConstantOrRegister(ins, LSetPropertyCache::Value, ins->mir()->value()->type());
 
-    addSetPropertyCache(ins, liveRegs, objReg, tempReg, ins->mir()->name(), value,
+    addSetPropertyCache(ins, liveRegs, objReg, tempReg, id, value,
                         ins->mir()->strict(), ins->mir()->needsTypeBarrier(),
                         ins->mir()->profilerLeavePc());
 }
 
-typedef bool (*SetPropertyICFn)(JSContext*, HandleScript, size_t, HandleObject, HandleValue);
+typedef bool (*SetPropertyICFn)(JSContext*, HandleScript, size_t, HandleObject, HandleValue,
+                                HandleValue);
 const VMFunction SetPropertyIC::UpdateInfo = FunctionInfo<SetPropertyICFn>(SetPropertyIC::update);
 
 void
 CodeGenerator::visitSetPropertyIC(OutOfLineUpdateCache* ool, DataPtr<SetPropertyIC>& ic)
 {
     LInstruction* lir = ool->lir();
     saveLive(lir);
 
     pushArg(ic->value());
+    pushArg(ic->id());
     pushArg(ic->object());
     pushArg(Imm32(ool->getCacheIndex()));
     pushArg(ImmGCPtr(gen->info().script()));
     callVM(SetPropertyIC::UpdateInfo, lir);
     restoreLive(lir);
 
     masm.jump(ool->rejoin());
 }
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -394,17 +394,17 @@ class CodeGenerator : public CodeGenerat
     }
 
   private:
     void addGetPropertyCache(LInstruction* ins, LiveRegisterSet liveRegs, Register objReg,
                              ConstantOrRegister id, TypedOrValueRegister output,
                              bool monitoredResult, bool allowDoubleResult,
                              jsbytecode* profilerLeavePc);
     void addSetPropertyCache(LInstruction* ins, LiveRegisterSet liveRegs, Register objReg,
-                             Register tempReg, PropertyName* name, ConstantOrRegister value,
+                             Register tempReg, ConstantOrRegister id, ConstantOrRegister value,
                              bool strict, bool needsTypeBarrier, jsbytecode* profilerLeavePc);
     void addSetElementCache(LInstruction* ins, Register obj, Register unboxIndex, Register temp,
                             FloatRegister tempDouble, FloatRegister tempFloat32,
                             ValueOperand index, ConstantOrRegister value,
                             bool strict, bool guardHoles, jsbytecode* profilerLeavePc);
 
     bool generateBranchV(const ValueOperand& value, Label* ifTrue, Label* ifFalse, FloatRegister fr);
 
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -12469,17 +12469,18 @@ bool
 IonBuilder::setPropTryCache(bool* emitted, MDefinition* obj,
                             PropertyName* name, MDefinition* value,
                             bool barrier, TemporaryTypeSet* objTypes)
 {
     MOZ_ASSERT(*emitted == false);
 
     bool strict = IsStrictSetPC(pc);
 
-    MSetPropertyCache* ins = MSetPropertyCache::New(alloc(), obj, value, name, strict, barrier);
+    MConstant* id = constant(StringValue(name));
+    MSetPropertyCache* ins = MSetPropertyCache::New(alloc(), obj, id, value, strict, barrier);
     current->add(ins);
     current->push(value);
 
     if (!resumeAfter(ins))
         return false;
 
     trackOptimizationSuccess();
     *emitted = true;
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -2493,35 +2493,34 @@ EmitCallProxySet(JSContext* cx, MacroAss
     masm.adjustStack(IonOOLProxyExitFrameLayout::Size());
 
     masm.icRestoreLive(liveRegs, aic);
     return true;
 }
 
 bool
 SetPropertyIC::attachGenericProxy(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                                  void* returnAddr)
+                                  HandleId id, void* returnAddr)
 {
     MOZ_ASSERT(!hasGenericProxyStub());
 
     MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     StubAttacher attacher(*this);
 
     Label failures;
     {
         masm.branchTestObjectIsProxy(false, object(), temp(), &failures);
 
         // Remove the DOM proxies. They'll take care of themselves so this stub doesn't
         // catch too much. The failure case is actually Equal. Fall through to the failure code.
         masm.branchTestProxyHandlerFamily(Assembler::Equal, object(), temp(),
                                           GetDOMProxyHandlerFamily(), &failures);
     }
 
-    RootedId propId(cx, AtomToId(name()));
-    if (!EmitCallProxySet(cx, masm, attacher, propId, liveRegs_, object(), value(),
+    if (!EmitCallProxySet(cx, masm, attacher, id, liveRegs_, object(), value(),
                           returnAddr, strict()))
     {
         return false;
     }
 
     attacher.jumpRejoin(masm);
 
     masm.bind(&failures);
@@ -2531,35 +2530,34 @@ SetPropertyIC::attachGenericProxy(JSCont
     hasGenericProxyStub_ = true;
 
     return linkAndAttachStub(cx, masm, attacher, ion, "generic proxy set",
                              JS::TrackedOutcome::ICSetPropStub_GenericProxy);
 }
 
 bool
 SetPropertyIC::attachDOMProxyShadowed(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                                      HandleObject obj, void* returnAddr)
+                                      HandleObject obj, HandleId id, void* returnAddr)
 {
     MOZ_ASSERT(IsCacheableDOMProxy(obj));
 
     Label failures;
     MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     StubAttacher attacher(*this);
 
     // Guard on the shape of the object.
     masm.branchPtr(Assembler::NotEqual,
                    Address(object(), JSObject::offsetOfShape()),
                    ImmGCPtr(obj->maybeShape()), &failures);
 
     // No need for more guards: we know this is a DOM proxy, since the shape
     // guard enforces a given JSClass, so just go ahead and emit the call to
     // ProxySet.
 
-    RootedId propId(cx, AtomToId(name()));
-    if (!EmitCallProxySet(cx, masm, attacher, propId, liveRegs_, object(),
+    if (!EmitCallProxySet(cx, masm, attacher, id, liveRegs_, object(),
                           value(), returnAddr, strict()))
     {
         return false;
     }
 
     // Success.
     attacher.jumpRejoin(masm);
 
@@ -2781,68 +2779,66 @@ GenerateCallSetter(JSContext* cx, IonScr
         masm.freeStack(masm.framePushed() - framePushedBefore);
     }
 
     masm.icRestoreLive(liveRegs, aic);
     return true;
 }
 
 static bool
-IsCacheableDOMProxyUnshadowedSetterCall(JSContext* cx, HandleObject obj, HandlePropertyName name,
+IsCacheableDOMProxyUnshadowedSetterCall(JSContext* cx, HandleObject obj, HandleId id,
                                         MutableHandleObject holder, MutableHandleShape shape)
 {
     MOZ_ASSERT(IsCacheableDOMProxy(obj));
 
     RootedObject checkObj(cx, obj->getTaggedProto().toObjectOrNull());
     if (!checkObj)
         return false;
 
-    if (!LookupPropertyPure(cx, obj, NameToId(name), holder.address(), shape.address()))
+    if (!LookupPropertyPure(cx, obj, id, holder.address(), shape.address()))
         return false;
 
     if (!holder)
         return false;
 
     return IsCacheableSetPropCallNative(checkObj, holder, shape) ||
            IsCacheableSetPropCallPropertyOp(checkObj, holder, shape) ||
            IsCacheableSetPropCallScripted(checkObj, holder, shape);
 }
 
 bool
 SetPropertyIC::attachDOMProxyUnshadowed(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                                        HandleObject obj, void* returnAddr)
+                                        HandleObject obj, HandleId id, void* returnAddr)
 {
     MOZ_ASSERT(IsCacheableDOMProxy(obj));
 
     Label failures;
     MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     StubAttacher attacher(*this);
 
     // Guard on the shape of the object.
     masm.branchPtr(Assembler::NotEqual,
                    Address(object(), JSObject::offsetOfShape()),
                    ImmGCPtr(obj->maybeShape()), &failures);
 
     // Guard that our expando object hasn't started shadowing this property.
-    CheckDOMProxyExpandoDoesNotShadow(cx, masm, obj, NameToId(name()), object(), &failures);
-
-    RootedPropertyName propName(cx, name());
+    CheckDOMProxyExpandoDoesNotShadow(cx, masm, obj, id, object(), &failures);
+
     RootedObject holder(cx);
     RootedShape shape(cx);
-    if (IsCacheableDOMProxyUnshadowedSetterCall(cx, obj, propName, &holder, &shape)) {
+    if (IsCacheableDOMProxyUnshadowedSetterCall(cx, obj, id, &holder, &shape)) {
         if (!GenerateCallSetter(cx, ion, masm, attacher, obj, holder, shape, strict(),
                                 object(), temp(), value(), &failures, liveRegs_, returnAddr))
         {
             return false;
         }
     } else {
         // Either there was no proto, or the property wasn't appropriately found on it.
         // Drop back to just a call to Proxy::set().
-        RootedId propId(cx, AtomToId(name()));
-        if (!EmitCallProxySet(cx, masm, attacher, propId, liveRegs_, object(),
+        if (!EmitCallProxySet(cx, masm, attacher, id, liveRegs_, object(),
                             value(), returnAddr, strict()))
         {
             return false;
         }
     }
 
     // Success.
     attacher.jumpRejoin(masm);
@@ -3371,35 +3367,35 @@ SetPropertyIC::tryAttachProxy(JSContext*
 
     void* returnAddr = GetReturnAddressToIonCode(cx);
     if (IsCacheableDOMProxy(obj)) {
         DOMProxyShadowsResult shadows = GetDOMProxyShadowsCheck()(cx, obj, id);
         if (shadows == ShadowCheckFailed)
             return false;
 
         if (DOMProxyIsShadowing(shadows)) {
-            if (!attachDOMProxyShadowed(cx, outerScript, ion, obj, returnAddr))
+            if (!attachDOMProxyShadowed(cx, outerScript, ion, obj, id, returnAddr))
                 return false;
             *emitted = true;
             return true;
         }
 
         MOZ_ASSERT(shadows == DoesntShadow || shadows == DoesntShadowUnique);
         if (shadows == DoesntShadowUnique)
             reset(Reprotect);
-        if (!attachDOMProxyUnshadowed(cx, outerScript, ion, obj, returnAddr))
+        if (!attachDOMProxyUnshadowed(cx, outerScript, ion, obj, id, returnAddr))
             return false;
         *emitted = true;
         return true;
     }
 
     if (hasGenericProxyStub())
         return true;
 
-    if (!attachGenericProxy(cx, outerScript, ion, returnAddr))
+    if (!attachGenericProxy(cx, outerScript, ion, id, returnAddr))
         return false;
     *emitted = true;
     return true;
 }
 
 bool
 SetPropertyIC::tryAttachNative(JSContext* cx, HandleScript outerScript, IonScript* ion,
                                HandleObject obj, HandleId id, bool* emitted, bool* tryNativeAddSlot)
@@ -3457,24 +3453,28 @@ SetPropertyIC::tryAttachUnboxedExpando(J
     if (!attachSetSlot(cx, outerScript, ion, obj, shape, checkTypeset))
         return false;
     *emitted = true;
     return true;
 }
 
 bool
 SetPropertyIC::tryAttachStub(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                             HandleObject obj, HandleId id, bool* emitted, bool* tryNativeAddSlot)
+                             HandleObject obj, HandleValue idval, MutableHandleId id,
+                             bool* emitted, bool* tryNativeAddSlot)
 {
     MOZ_ASSERT(!*emitted);
     MOZ_ASSERT(!*tryNativeAddSlot);
 
     if (!canAttachStub() || obj->watched())
         return true;
 
+    if (!ValueToId<CanGC>(cx, idval, id))
+        return false;
+
     if (!*emitted && !tryAttachProxy(cx, outerScript, ion, obj, id, emitted))
         return false;
 
     if (!*emitted && !tryAttachNative(cx, outerScript, ion, obj, id, emitted, tryNativeAddSlot))
         return false;
 
     if (!*emitted && !tryAttachUnboxed(cx, outerScript, ion, obj, id, emitted))
         return false;
@@ -3524,22 +3524,20 @@ SetPropertyIC::tryAttachAddSlot(JSContex
         return true;
     }
 
     return true;
 }
 
 bool
 SetPropertyIC::update(JSContext* cx, HandleScript outerScript, size_t cacheIndex, HandleObject obj,
-                      HandleValue value)
+                      HandleValue idval, HandleValue value)
 {
     IonScript* ion = outerScript->ionScript();
     SetPropertyIC& cache = ion->getCache(cacheIndex).toSetProperty();
-    RootedPropertyName name(cx, cache.name());
-    RootedId id(cx, AtomToId(name));
 
     // Remember the old group and shape if we may attach an add-property stub.
     // Also, some code under tryAttachStub depends on obj having a non-lazy
     // group, see for instance CanInlineSetPropTypeCheck.
     RootedObjectGroup oldGroup(cx);
     RootedShape oldShape(cx);
     if (cache.canAttachStub()) {
         oldGroup = obj->getGroup(cx);
@@ -3549,29 +3547,31 @@ SetPropertyIC::update(JSContext* cx, Han
         oldShape = obj->maybeShape();
         if (obj->is<UnboxedPlainObject>()) {
             MOZ_ASSERT(!oldShape);
             if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando())
                 oldShape = expando->lastProperty();
         }
     }
 
+    RootedId id(cx);
     bool emitted = false;
     bool tryNativeAddSlot = false;
-    if (!cache.tryAttachStub(cx, outerScript, ion, obj, id, &emitted, &tryNativeAddSlot))
+    if (!cache.tryAttachStub(cx, outerScript, ion, obj, idval, &id, &emitted, &tryNativeAddSlot))
         return false;
 
     // Set/Add the property on the object, the inlined cache are setup for the next execution.
     if (JSOp(*cache.pc()) == JSOP_INITGLEXICAL) {
         RootedScript script(cx);
         jsbytecode* pc;
         cache.getScriptedLocation(&script, &pc);
         MOZ_ASSERT(!script->hasNonSyntacticScope());
         InitGlobalLexicalOperation(cx, &cx->global()->lexicalScope(), script, pc, value);
     } else {
+        RootedPropertyName name(cx, idval.toString()->asAtom().asPropertyName());
         if (!SetProperty(cx, obj, name, value, cache.strict(), cache.pc()))
             return false;
     }
 
     if (!emitted &&
         !cache.tryAttachAddSlot(cx, outerScript, ion, obj, id, oldGroup, oldShape,
                                 tryNativeAddSlot, &emitted))
     {
--- a/js/src/jit/IonCaches.h
+++ b/js/src/jit/IonCaches.h
@@ -566,30 +566,30 @@ class SetPropertyIC : public IonCache
 {
   protected:
     // Registers live after the cache, excluding output registers. The initial
     // value of these registers must be preserved by the cache.
     LiveRegisterSet liveRegs_;
 
     Register object_;
     Register temp_;
-    PropertyName* name_;
+    ConstantOrRegister id_;
     ConstantOrRegister value_;
     bool strict_;
     bool needsTypeBarrier_;
 
     bool hasGenericProxyStub_;
 
   public:
-    SetPropertyIC(LiveRegisterSet liveRegs, Register object, Register temp, PropertyName* name,
+    SetPropertyIC(LiveRegisterSet liveRegs, Register object, Register temp, ConstantOrRegister id,
                   ConstantOrRegister value, bool strict, bool needsTypeBarrier)
       : liveRegs_(liveRegs),
         object_(object),
         temp_(temp),
-        name_(name),
+        id_(id),
         value_(value),
         strict_(strict),
         needsTypeBarrier_(needsTypeBarrier),
         hasGenericProxyStub_(false)
     {
     }
 
     CACHE_HEADER(SetProperty)
@@ -597,18 +597,18 @@ class SetPropertyIC : public IonCache
     void reset(ReprotectCode reprotect);
 
     Register object() const {
         return object_;
     }
     Register temp() const {
         return temp_;
     }
-    PropertyName* name() const {
-        return name_;
+    ConstantOrRegister id() const {
+        return id_;
     }
     ConstantOrRegister value() const {
         return value_;
     }
     bool strict() const {
         return strict_;
     }
     bool needsTypeBarrier() const {
@@ -632,41 +632,41 @@ class SetPropertyIC : public IonCache
                           HandleObject obj, HandleObject holder, HandleShape shape,
                           void* returnAddr);
 
     bool attachAddSlot(JSContext* cx, HandleScript outerScript, IonScript* ion,
                        HandleObject obj, HandleShape oldShape, HandleObjectGroup oldGroup,
                        bool checkTypeset);
 
     bool attachGenericProxy(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                            void* returnAddr);
+                            HandleId id, void* returnAddr);
 
     bool attachDOMProxyShadowed(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                                HandleObject obj, void* returnAddr);
+                                HandleObject obj, HandleId id, void* returnAddr);
 
     bool attachDOMProxyUnshadowed(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                                  HandleObject obj, void* returnAddr);
+                                  HandleObject obj, HandleId id, void* returnAddr);
 
     static bool update(JSContext* cx, HandleScript outerScript, size_t cacheIndex,
-                       HandleObject obj, HandleValue value);
+                       HandleObject obj, HandleValue idval, HandleValue value);
 
     bool tryAttachNative(JSContext* cx, HandleScript outerScript, IonScript* ion,
                          HandleObject obj, HandleId id, bool* emitted, bool* tryNativeAddSlot);
 
     bool tryAttachUnboxed(JSContext* cx, HandleScript outerScript, IonScript* ion,
                           HandleObject obj, HandleId id, bool* emitted);
 
     bool tryAttachUnboxedExpando(JSContext* cx, HandleScript outerScript, IonScript* ion,
                                  HandleObject obj, HandleId id, bool* emitted);
 
     bool tryAttachProxy(JSContext* cx, HandleScript outerScript, IonScript* ion,
                         HandleObject obj, HandleId id, bool* emitted);
 
     bool tryAttachStub(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                       HandleObject obj, HandleId id, bool* emitted,
+                       HandleObject obj, HandleValue idval, MutableHandleId id, bool* emitted,
                        bool* tryNativeAddSlot);
 
     bool tryAttachAddSlot(JSContext* cx, HandleScript outerScript, IonScript* ion,
                           HandleObject obj, HandleId id, HandleObjectGroup oldGroup,
                           HandleShape oldShape, bool tryNativeAddSlot, bool* emitted);
 };
 
 class SetElementIC : public IonCache
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -3491,22 +3491,33 @@ LIRGenerator::visitDeleteElement(MDelete
     assignSafepoint(lir, ins);
 }
 
 void
 LIRGenerator::visitSetPropertyCache(MSetPropertyCache* ins)
 {
     MOZ_ASSERT(ins->object()->type() == MIRType_Object);
 
+    MDefinition* id = ins->idval();
+    MOZ_ASSERT(id->type() == MIRType_String ||
+               id->type() == MIRType_Symbol ||
+               id->type() == MIRType_Int32 ||
+               id->type() == MIRType_Value);
+
+    // If this is a SETPROP, the id is a constant string. Allow passing it as a
+    // constant to reduce register allocation pressure.
+    bool useConstId = id->type() == MIRType_String || id->type() == MIRType_Symbol;
+
     // Set the performs-call flag so that we don't omit the overrecursed check.
     // This is necessary because the cache can attach a scripted setter stub
     // that calls this script recursively.
     gen->setPerformsCall();
 
     LInstruction* lir = new(alloc()) LSetPropertyCache(useRegister(ins->object()), temp());
+    useBoxOrTypedOrConstant(lir, LSetPropertyCache::Id, id, useConstId);
     useBoxOrTypedOrConstant(lir, LSetPropertyCache::Value, ins->value(), /* useConstant = */ true);
 
     add(lir, ins);
     assignSafepoint(lir, ins);
 }
 
 void
 LIRGenerator::visitSetElementCache(MSetElementCache* ins)
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -11369,40 +11369,55 @@ class MCallSetProperty
     }
 
     bool possiblyCalls() const override {
         return true;
     }
 };
 
 class MSetPropertyCache
-  : public MSetPropertyInstruction,
-    public MixPolicy<SingleObjectPolicy, NoFloatPolicy<1> >::Data
-{
+  : public MTernaryInstruction,
+    public Mix3Policy<SingleObjectPolicy, CacheIdPolicy<1>, NoFloatPolicy<2>>::Data
+{
+    bool strict_;
     bool needsTypeBarrier_;
 
-    MSetPropertyCache(MDefinition* obj, MDefinition* value, PropertyName* name, bool strict,
+    MSetPropertyCache(MDefinition* obj, MDefinition* id, MDefinition* value, bool strict,
                       bool typeBarrier)
-      : MSetPropertyInstruction(obj, value, name, strict),
+      : MTernaryInstruction(obj, id, value),
+        strict_(strict),
         needsTypeBarrier_(typeBarrier)
     {
     }
 
   public:
     INSTRUCTION_HEADER(SetPropertyCache)
 
-    static MSetPropertyCache* New(TempAllocator& alloc, MDefinition* obj, MDefinition* value,
-                                  PropertyName* name, bool strict, bool typeBarrier)
-    {
-        return new(alloc) MSetPropertyCache(obj, value, name, strict, typeBarrier);
+    static MSetPropertyCache* New(TempAllocator& alloc, MDefinition* obj, MDefinition* id,
+                                  MDefinition* value, bool strict, bool typeBarrier)
+    {
+        return new(alloc) MSetPropertyCache(obj, id, value, strict, typeBarrier);
     }
 
     bool needsTypeBarrier() const {
         return needsTypeBarrier_;
     }
+
+    MDefinition* object() const {
+        return getOperand(0);
+    }
+    MDefinition* idval() const {
+        return getOperand(1);
+    }
+    MDefinition* value() const {
+        return getOperand(2);
+    }
+    bool strict() const {
+        return strict_;
+    }
 };
 
 class MSetElementCache
   : public MSetElementInstruction,
     public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >::Data
 {
     bool guardHoles_;
 
--- a/js/src/jit/TypePolicy.cpp
+++ b/js/src/jit/TypePolicy.cpp
@@ -1215,16 +1215,17 @@ FilterTypeSetPolicy::adjustInputs(TempAl
     _(Mix3Policy<ObjectPolicy<0>, ObjectPolicy<1>, IntPolicy<2> >)      \
     _(Mix3Policy<ObjectPolicy<0>, ObjectPolicy<1>, ObjectPolicy<2> >)   \
     _(Mix3Policy<StringPolicy<0>, IntPolicy<1>, IntPolicy<2>>)          \
     _(Mix3Policy<StringPolicy<0>, ObjectPolicy<1>, StringPolicy<2> >)   \
     _(Mix3Policy<StringPolicy<0>, StringPolicy<1>, StringPolicy<2> >)   \
     _(Mix4Policy<ObjectPolicy<0>, StringPolicy<1>, BoxPolicy<2>, BoxPolicy<3>>) \
     _(Mix4Policy<ObjectPolicy<0>, IntPolicy<1>, IntPolicy<2>, IntPolicy<3>>) \
     _(Mix4Policy<ObjectPolicy<0>, IntPolicy<1>, TruncateToInt32Policy<2>, TruncateToInt32Policy<3> >) \
+    _(Mix3Policy<ObjectPolicy<0>, CacheIdPolicy<1>, NoFloatPolicy<2>>)  \
     _(Mix4Policy<SimdScalarPolicy<0>, SimdScalarPolicy<1>, SimdScalarPolicy<2>, SimdScalarPolicy<3> >) \
     _(MixPolicy<BoxPolicy<0>, ObjectPolicy<1> >)                        \
     _(MixPolicy<ConvertToStringPolicy<0>, ConvertToStringPolicy<1> >)   \
     _(MixPolicy<ConvertToStringPolicy<0>, ObjectPolicy<1> >)            \
     _(MixPolicy<DoublePolicy<0>, DoublePolicy<1> >)                     \
     _(MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >)                        \
     _(MixPolicy<ObjectPolicy<0>, CacheIdPolicy<1>>)                     \
     _(MixPolicy<ObjectPolicy<0>, ConvertToStringPolicy<1> >)            \
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -5917,27 +5917,28 @@ class LCallDeleteElement : public LCallI
     static const size_t Index = BOX_PIECES;
 
     MDeleteElement* mir() const {
         return mir_->toDeleteElement();
     }
 };
 
 // Patchable jump to stubs generated for a SetProperty cache.
-class LSetPropertyCache : public LInstructionHelper<0, 1 + BOX_PIECES, 1>
+class LSetPropertyCache : public LInstructionHelper<0, 1 + 2 * BOX_PIECES, 1>
 {
   public:
     LIR_HEADER(SetPropertyCache)
 
     LSetPropertyCache(const LAllocation& object, const LDefinition& temp) {
         setOperand(0, object);
         setTemp(0, temp);
     }
 
-    static const size_t Value = 1;
+    static const size_t Id = 1;
+    static const size_t Value = 1 + BOX_PIECES;
 
     const MSetPropertyCache* mir() const {
         return mir_->toSetPropertyCache();
     }
 };
 
 class LSetElementCacheV : public LInstructionHelper<0, 1 + 2 * BOX_PIECES, 4>
 {