Bug 918584 - Part 3: Add SetPropertyParIC. (r=jandem)
authorShu-yu Guo <shu@rfrn.org>
Thu, 10 Oct 2013 20:02:31 -0700
changeset 164235 ec6607f70f5217f3c54f55935ec54770f9755832
parent 164234 ce1959c9b3cb0de85b9dc888a7ac429e22eb4034
child 164236 7924e5fb323a95cc1064cac1ef18572f9c9c7d54
push id3066
push userakeybl@mozilla.com
push dateMon, 09 Dec 2013 19:58:46 +0000
treeherdermozilla-beta@a31a0dce83aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs918584
milestone27.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 918584 - Part 3: Add SetPropertyParIC. (r=jandem)
js/src/jit-test/tests/parallel/ic-setproperty.js
js/src/jit/CodeGenerator.cpp
js/src/jit/CodeGenerator.h
js/src/jit/IonAnalysis.cpp
js/src/jit/IonCaches.cpp
js/src/jit/IonCaches.h
js/src/jit/LIR-Common.h
js/src/jit/Lowering.cpp
js/src/jit/MIR.h
js/src/jit/ParallelFunctions.cpp
js/src/jit/ParallelFunctions.h
js/src/jit/ParallelSafetyAnalysis.cpp
js/src/jit/arm/Lowering-arm.cpp
js/src/jit/arm/Lowering-arm.h
js/src/jit/x64/Lowering-x64.cpp
js/src/jit/x64/Lowering-x64.h
js/src/jit/x86/CodeGenerator-x86.cpp
js/src/jit/x86/Lowering-x86.cpp
js/src/jit/x86/Lowering-x86.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/parallel/ic-setproperty.js
@@ -0,0 +1,111 @@
+load(libdir + "parallelarray-helpers.js");
+
+function set(o, v) {
+  // Padding to prevent inlining.
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  o.foo = v;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+  var foo = 0;
+}
+set({ foo: 0 }, 42);
+
+function testSetPropertySlot() {
+  assertArraySeqParResultsEq(
+    range(0, minItemsTestingThreshold),
+    "map",
+    function (i) {
+      var o1 = {};
+      var o2 = {};
+      var o3 = {};
+      var o4 = {};
+      // Defines .foo
+      set(o1, i + 1);
+      set(o2, i + 2);
+      set(o3, i + 3);
+      set(o4, i + 4);
+      // Sets .foo
+      set(o1, i + 5);
+      set(o2, i + 6);
+      set(o3, i + 7);
+      set(o4, i + 8);
+      return o1.foo + o2.foo + o3.foo + o4.foo;
+    });
+}
+
+function testSetArrayLength() {
+  assertArraySeqParResultsEq(
+    range(0, minItemsTestingThreshold),
+    "map",
+    function (i) {
+      var a = [];
+      a.length = i;
+      return a.length;
+    });
+}
+
+if (getBuildConfiguration().parallelJS) {
+  testSetPropertySlot();
+  testSetArrayLength();
+}
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -6034,16 +6034,35 @@ CodeGenerator::addGetPropertyCache(LInst
         return addCache(ins, allocateCache(cache));
       }
       default:
         MOZ_ASSUME_UNREACHABLE("Bad execution mode");
     }
 }
 
 bool
+CodeGenerator::addSetPropertyCache(LInstruction *ins, RegisterSet liveRegs, Register objReg,
+                                   PropertyName *name, ConstantOrRegister value, bool strict,
+                                   bool needsTypeBarrier)
+{
+    switch (gen->info().executionMode()) {
+      case SequentialExecution: {
+          SetPropertyIC cache(liveRegs, objReg, name, value, strict, needsTypeBarrier);
+          return addCache(ins, allocateCache(cache));
+      }
+      case ParallelExecution: {
+          SetPropertyParIC cache(objReg, name, value, strict, needsTypeBarrier);
+          return addCache(ins, allocateCache(cache));
+      }
+      default:
+        MOZ_ASSUME_UNREACHABLE("Bad execution mode");
+    }
+}
+
+bool
 CodeGenerator::visitGetPropertyCacheV(LGetPropertyCacheV *ins)
 {
     RegisterSet liveRegs = ins->safepoint()->liveRegs();
     Register objReg = ToRegister(ins->getOperand(0));
     PropertyName *name = ins->mir()->name();
     bool allowGetters = ins->mir()->allowGetters();
     TypedOrValueRegister output = TypedOrValueRegister(GetValueOutput(ins));
 
@@ -6281,18 +6300,22 @@ CodeGenerator::visitBindNameIC(OutOfLine
     restoreLiveIgnore(lir, StoreRegisterTo(ic->outputReg()).clobbered());
 
     masm.jump(ool->rejoin());
     return true;
 }
 
 typedef bool (*SetPropertyFn)(JSContext *, HandleObject,
                               HandlePropertyName, const HandleValue, bool, jsbytecode *);
-static const VMFunction SetPropertyInfo =
-    FunctionInfo<SetPropertyFn>(SetProperty);
+typedef ParallelResult (*SetPropertyParFn)(ForkJoinSlice *, HandleObject,
+                                           HandlePropertyName, const HandleValue, bool,
+                                           jsbytecode *);
+static const VMFunctionsModal SetPropertyInfo = VMFunctionsModal(
+    FunctionInfo<SetPropertyFn>(SetProperty),
+    FunctionInfo<SetPropertyParFn>(SetPropertyPar));
 
 bool
 CodeGenerator::visitCallSetProperty(LCallSetProperty *ins)
 {
     ConstantOrRegister value = TypedOrValueRegister(ToValue(ins, LCallSetProperty::Value));
 
     const Register objReg = ToRegister(ins->getOperand(0));
 
@@ -6344,36 +6367,34 @@ CodeGenerator::visitCallDeleteElement(LC
 
 bool
 CodeGenerator::visitSetPropertyCacheV(LSetPropertyCacheV *ins)
 {
     RegisterSet liveRegs = ins->safepoint()->liveRegs();
     Register objReg = ToRegister(ins->getOperand(0));
     ConstantOrRegister value = TypedOrValueRegister(ToValue(ins, LSetPropertyCacheV::Value));
 
-    SetPropertyIC cache(liveRegs, objReg, ins->mir()->name(), value, ins->mir()->strict(),
-                        ins->mir()->needsTypeBarrier());
-    return addCache(ins, allocateCache(cache));
+    return addSetPropertyCache(ins, liveRegs, objReg, ins->mir()->name(), value,
+                               ins->mir()->strict(), ins->mir()->needsTypeBarrier());
 }
 
 bool
 CodeGenerator::visitSetPropertyCacheT(LSetPropertyCacheT *ins)
 {
     RegisterSet liveRegs = ins->safepoint()->liveRegs();
     Register objReg = ToRegister(ins->getOperand(0));
     ConstantOrRegister value;
 
     if (ins->getOperand(1)->isConstant())
         value = ConstantOrRegister(*ins->getOperand(1)->toConstant());
     else
         value = TypedOrValueRegister(ins->valueType(), ToAnyRegister(ins->getOperand(1)));
 
-    SetPropertyIC cache(liveRegs, objReg, ins->mir()->name(), value, ins->mir()->strict(),
-                        ins->mir()->needsTypeBarrier());
-    return addCache(ins, allocateCache(cache));
+    return addSetPropertyCache(ins, liveRegs, objReg, ins->mir()->name(), value,
+                               ins->mir()->strict(), ins->mir()->needsTypeBarrier());
 }
 
 typedef bool (*SetPropertyICFn)(JSContext *, size_t, HandleObject, HandleValue);
 const VMFunction SetPropertyIC::UpdateInfo =
     FunctionInfo<SetPropertyICFn>(SetPropertyIC::update);
 
 bool
 CodeGenerator::visitSetPropertyIC(OutOfLineUpdateCache *ool, DataPtr<SetPropertyIC> &ic)
@@ -6387,16 +6408,37 @@ CodeGenerator::visitSetPropertyIC(OutOfL
     if (!callVM(SetPropertyIC::UpdateInfo, lir))
         return false;
     restoreLive(lir);
 
     masm.jump(ool->rejoin());
     return true;
 }
 
+typedef ParallelResult (*SetPropertyParICFn)(ForkJoinSlice *, size_t, HandleObject, HandleValue);
+const VMFunction SetPropertyParIC::UpdateInfo =
+    FunctionInfo<SetPropertyParICFn>(SetPropertyParIC::update);
+
+bool
+CodeGenerator::visitSetPropertyParIC(OutOfLineUpdateCache *ool, DataPtr<SetPropertyParIC> &ic)
+{
+    LInstruction *lir = ool->lir();
+    saveLive(lir);
+
+    pushArg(ic->value());
+    pushArg(ic->object());
+    pushArg(Imm32(ool->getCacheIndex()));
+    if (!callVM(SetPropertyParIC::UpdateInfo, lir))
+        return false;
+    restoreLive(lir);
+
+    masm.jump(ool->rejoin());
+    return true;
+}
+
 typedef bool (*ThrowFn)(JSContext *, HandleValue);
 static const VMFunction ThrowInfo = FunctionInfo<ThrowFn>(js::Throw);
 
 bool
 CodeGenerator::visitThrow(LThrow *lir)
 {
     pushArg(ToValue(lir, LThrow::Value));
     return callVM(ThrowInfo, lir);
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -307,16 +307,17 @@ class CodeGenerator : public CodeGenerat
     bool visitSetPropertyCacheV(LSetPropertyCacheV *ins);
     bool visitSetPropertyCacheT(LSetPropertyCacheT *ins);
     bool visitGetNameCache(LGetNameCache *ins);
     bool visitCallsiteCloneCache(LCallsiteCloneCache *ins);
 
     bool visitGetPropertyIC(OutOfLineUpdateCache *ool, DataPtr<GetPropertyIC> &ic);
     bool visitGetPropertyParIC(OutOfLineUpdateCache *ool, DataPtr<GetPropertyParIC> &ic);
     bool visitSetPropertyIC(OutOfLineUpdateCache *ool, DataPtr<SetPropertyIC> &ic);
+    bool visitSetPropertyParIC(OutOfLineUpdateCache *ool, DataPtr<SetPropertyParIC> &ic);
     bool visitGetElementIC(OutOfLineUpdateCache *ool, DataPtr<GetElementIC> &ic);
     bool visitGetElementParIC(OutOfLineUpdateCache *ool, DataPtr<GetElementParIC> &ic);
     bool visitSetElementIC(OutOfLineUpdateCache *ool, DataPtr<SetElementIC> &ic);
     bool visitBindNameIC(OutOfLineUpdateCache *ool, DataPtr<BindNameIC> &ic);
     bool visitNameIC(OutOfLineUpdateCache *ool, DataPtr<NameIC> &ic);
     bool visitCallsiteCloneIC(OutOfLineUpdateCache *ool, DataPtr<CallsiteCloneIC> &ic);
 
     bool visitAssertRangeI(LAssertRangeI *ins);
@@ -331,16 +332,19 @@ class CodeGenerator : public CodeGenerat
     }
 
   private:
     bool addGetPropertyCache(LInstruction *ins, RegisterSet liveRegs, Register objReg,
                              PropertyName *name, TypedOrValueRegister output,
                              bool allowGetters);
     bool addGetElementCache(LInstruction *ins, Register obj, ConstantOrRegister index,
                             TypedOrValueRegister output, bool monitoredResult);
+    bool addSetPropertyCache(LInstruction *ins, RegisterSet liveRegs, Register objReg,
+                             PropertyName *name, ConstantOrRegister value, bool strict,
+                             bool needsTypeBarrier);
     bool checkForAbortPar(LInstruction *lir);
 
     bool generateBranchV(const ValueOperand &value, Label *ifTrue, Label *ifFalse, FloatRegister fr);
 
     bool emitAllocateGCThingPar(LInstruction *lir, const Register &objReg, const Register &sliceReg,
                                 const Register &tempReg1, const Register &tempReg2,
                                 JSObject *templateObj);
 
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -1911,17 +1911,17 @@ AnalyzePoppedThis(JSContext *cx, types::
                   bool *phandled)
 {
     // Determine the effect that a use of the |this| value when calling |new|
     // on a script has on the properties definitely held by the new object.
 
     if (ins->isCallSetProperty()) {
         MCallSetProperty *setprop = ins->toCallSetProperty();
 
-        if (setprop->obj() != thisValue)
+        if (setprop->object() != thisValue)
             return true;
 
         // Don't use GetAtomId here, we need to watch for SETPROP on
         // integer properties and bail out. We can't mark the aggregate
         // JSID_VOID type property as being in a definite slot.
         if (setprop->name() == cx->names().classPrototype ||
             setprop->name() == cx->names().proto ||
             setprop->name() == cx->names().constructor)
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -2834,16 +2834,118 @@ SetPropertyIC::update(JSContext *cx, siz
 
 void
 SetPropertyIC::reset()
 {
     RepatchIonCache::reset();
     hasGenericProxyStub_ = false;
 }
 
+ParallelResult
+SetPropertyParIC::update(ForkJoinSlice *slice, size_t cacheIndex, HandleObject obj,
+                         HandleValue value)
+{
+    JS_ASSERT(slice->isThreadLocal(obj));
+
+    AutoFlushCache afc("SetPropertyParCache", slice->runtime()->ionRuntime());
+
+    IonScript *ion = GetTopIonJSScript(slice)->parallelIonScript();
+    SetPropertyParIC &cache = ion->getCache(cacheIndex).toSetPropertyPar();
+
+    RootedValue v(slice, value);
+    RootedId id(slice, AtomToId(cache.name()));
+
+    // Avoid unnecessary locking if cannot attach stubs.
+    if (!cache.canAttachStub()) {
+        if (!baseops::SetPropertyHelper<ParallelExecution>(slice, obj, obj, id, 0,
+                                                           &v, cache.strict()))
+        {
+            return TP_RETRY_SEQUENTIALLY;
+        }
+        return TP_SUCCESS;
+    }
+
+    SetPropertyIC::NativeSetPropCacheability canCache = SetPropertyIC::CanAttachNone;
+    bool attachedStub = false;
+
+    {
+        LockedJSContext cx(slice);
+
+        if (cache.canAttachStub()) {
+            bool alreadyStubbed;
+            if (!cache.hasOrAddStubbedShape(cx, obj->lastProperty(), &alreadyStubbed))
+                return TP_FATAL;
+            if (alreadyStubbed) {
+                if (!baseops::SetPropertyHelper<ParallelExecution>(slice, obj, obj, id, 0,
+                                                                   &v, cache.strict()))
+                {
+                    return TP_RETRY_SEQUENTIALLY;
+                }
+                return TP_SUCCESS;
+            }
+
+            // If the object has a lazy type, we need to de-lazify it, but
+            // this is not safe in parallel.
+            if (obj->hasLazyType())
+                return TP_RETRY_SEQUENTIALLY;
+
+            {
+                RootedShape shape(slice);
+                RootedObject holder(slice);
+                bool checkTypeset;
+                canCache = CanAttachNativeSetProp(obj, id, cache.value(), cache.needsTypeBarrier(),
+                                                  &holder, &shape, &checkTypeset);
+
+                if (canCache == SetPropertyIC::CanAttachSetSlot) {
+                    if (!cache.attachSetSlot(cx, ion, obj, shape, checkTypeset))
+                        return TP_FATAL;
+                    attachedStub = true;
+                }
+            }
+        }
+    }
+
+    uint32_t oldSlots = obj->numDynamicSlots();
+    RootedShape oldShape(slice, obj->lastProperty());
+
+    if (!baseops::SetPropertyHelper<ParallelExecution>(slice, obj, obj, id, 0, &v, cache.strict()))
+        return TP_RETRY_SEQUENTIALLY;
+
+    if (!attachedStub && canCache == SetPropertyIC::MaybeCanAttachAddSlot &&
+        !cache.needsTypeBarrier() &&
+        IsPropertyAddInlineable(obj, id, oldSlots, oldShape))
+    {
+        LockedJSContext cx(slice);
+        if (cache.canAttachStub() && !cache.attachAddSlot(cx, ion, obj, oldShape))
+            return TP_FATAL;
+    }
+
+    return TP_SUCCESS;
+}
+
+bool
+SetPropertyParIC::attachSetSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, Shape *shape,
+                                  bool checkTypeset)
+{
+    MacroAssembler masm(cx);
+    DispatchStubPrepender attacher(*this);
+    GenerateSetSlot(cx, masm, attacher, obj, shape, object(), value(), needsTypeBarrier(),
+                    checkTypeset);
+    return linkAndAttachStub(cx, masm, attacher, ion, "parallel setting");
+}
+
+bool
+SetPropertyParIC::attachAddSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, Shape *oldShape)
+{
+    MacroAssembler masm(cx);
+    DispatchStubPrepender attacher(*this);
+    GenerateAddSlot(cx, masm, attacher, obj, oldShape, object(), value());
+    return linkAndAttachStub(cx, masm, attacher, ion, "parallel adding");
+}
+
 const size_t GetElementIC::MAX_FAILED_UPDATES = 16;
 
 /* static */ bool
 GetElementIC::canAttachGetProp(JSObject *obj, const Value &idval, jsid id)
 {
     uint32_t dummy;
     return (obj->isNative() &&
             idval.isString() &&
--- a/js/src/jit/IonCaches.h
+++ b/js/src/jit/IonCaches.h
@@ -24,17 +24,18 @@ namespace jit {
     _(GetProperty)                                              \
     _(SetProperty)                                              \
     _(GetElement)                                               \
     _(SetElement)                                               \
     _(BindName)                                                 \
     _(Name)                                                     \
     _(CallsiteClone)                                            \
     _(GetPropertyPar)                                           \
-    _(GetElementPar)
+    _(GetElementPar)                                            \
+    _(SetPropertyPar)
 
 // Forward declarations of Cache kinds.
 #define FORWARD_DECLARE(kind) class kind##IC;
 IONCACHE_KIND_LIST(FORWARD_DECLARE)
 #undef FORWARD_DECLARE
 
 class IonCacheVisitor
 {
@@ -1109,16 +1110,68 @@ class GetElementParIC : public ParallelI
     bool attachTypedArrayElement(LockedJSContext &cx, IonScript *ion, TypedArrayObject *tarr,
                                  const Value &idval);
 
     static ParallelResult update(ForkJoinSlice *slice, size_t cacheIndex, HandleObject obj, HandleValue idval,
                                  MutableHandleValue vp);
 
 };
 
+class SetPropertyParIC : public ParallelIonCache
+{
+  protected:
+    Register object_;
+    PropertyName *name_;
+    ConstantOrRegister value_;
+    bool strict_;
+    bool needsTypeBarrier_;
+
+  public:
+    SetPropertyParIC(Register object, PropertyName *name, ConstantOrRegister value,
+                     bool strict, bool needsTypeBarrier)
+      : object_(object),
+        name_(name),
+        value_(value),
+        strict_(strict),
+        needsTypeBarrier_(needsTypeBarrier)
+    {
+    }
+
+    CACHE_HEADER(SetPropertyPar)
+
+#ifdef JS_CPU_X86
+    // x86 lacks a general purpose scratch register for dispatch caches and
+    // must be given one manually.
+    void initializeAddCacheState(LInstruction *ins, AddCacheState *addState);
+#endif
+
+    Register object() const {
+        return object_;
+    }
+    PropertyName *name() const {
+        return name_;
+    }
+    ConstantOrRegister value() const {
+        return value_;
+    }
+    bool strict() const {
+        return strict_;
+    }
+    bool needsTypeBarrier() const {
+        return needsTypeBarrier_;
+    }
+
+    bool attachSetSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, Shape *shape,
+                       bool checkTypeset);
+    bool attachAddSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, Shape *oldShape);
+
+    static ParallelResult update(ForkJoinSlice *slice, size_t cacheIndex, HandleObject obj,
+                                 HandleValue value);
+};
+
 #undef CACHE_HEADER
 
 // Implement cache casts now that the compiler can see the inheritance.
 #define CACHE_CASTS(ickind)                                             \
     ickind##IC &IonCache::to##ickind()                                  \
     {                                                                   \
         JS_ASSERT(is##ickind());                                        \
         return *static_cast<ickind##IC *>(this);                        \
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -4351,60 +4351,72 @@ class LCallDeleteElement : public LCallI
 
     MDeleteElement *mir() const {
         return mir_->toDeleteElement();
     }
 };
 
 // Patchable jump to stubs generated for a SetProperty cache, which stores a
 // boxed value.
-class LSetPropertyCacheV : public LInstructionHelper<0, 1 + BOX_PIECES, 1>
+class LSetPropertyCacheV : public LInstructionHelper<0, 1 + BOX_PIECES, 2>
 {
   public:
     LIR_HEADER(SetPropertyCacheV)
 
-    LSetPropertyCacheV(const LAllocation &object, const LDefinition &slots) {
+    LSetPropertyCacheV(const LAllocation &object, const LDefinition &slots,
+                       const LDefinition &temp) {
         setOperand(0, object);
         setTemp(0, slots);
+        setTemp(1, temp);
     }
 
     static const size_t Value = 1;
 
     const MSetPropertyCache *mir() const {
         return mir_->toSetPropertyCache();
     }
+
+    const LDefinition *tempForDispatchCache() {
+        return getTemp(1);
+    }
 };
 
 // Patchable jump to stubs generated for a SetProperty cache, which stores a
 // value of a known type.
-class LSetPropertyCacheT : public LInstructionHelper<0, 2, 1>
+class LSetPropertyCacheT : public LInstructionHelper<0, 2, 2>
 {
     MIRType valueType_;
 
   public:
     LIR_HEADER(SetPropertyCacheT)
 
     LSetPropertyCacheT(const LAllocation &object, const LDefinition &slots,
-                       const LAllocation &value, MIRType valueType)
+                       const LAllocation &value, const LDefinition &temp,
+                       MIRType valueType)
         : valueType_(valueType)
     {
         setOperand(0, object);
         setOperand(1, value);
         setTemp(0, slots);
+        setTemp(1, temp);
     }
 
     const MSetPropertyCache *mir() const {
         return mir_->toSetPropertyCache();
     }
     MIRType valueType() {
         return valueType_;
     }
     const char *extraName() const {
         return StringFromMIRType(valueType_);
     }
+
+    const LDefinition *tempForDispatchCache() {
+        return getTemp(1);
+    }
 };
 
 class LSetElementCacheV : public LInstructionHelper<0, 1 + 2 * BOX_PIECES, 3>
 {
   public:
     LIR_HEADER(SetElementCacheV);
 
     static const size_t Index = 1;
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2598,17 +2598,18 @@ LIRGenerator::visitGetPropertyCache(MGet
     JS_ASSERT(ins->object()->type() == MIRType_Object);
     if (ins->type() == MIRType_Value) {
         LGetPropertyCacheV *lir = new LGetPropertyCacheV(useRegister(ins->object()));
         if (!defineBox(lir, ins))
             return false;
         return assignSafepoint(lir, ins);
     }
 
-    LGetPropertyCacheT *lir = newLGetPropertyCacheT(ins);
+    LGetPropertyCacheT *lir = new LGetPropertyCacheT(useRegister(ins->object()),
+                                                     tempForDispatchCache(ins->type()));
     if (!define(lir, ins))
         return false;
     return assignSafepoint(lir, ins);
 }
 
 bool
 LIRGenerator::visitGetPropertyPolymorphic(MGetPropertyPolymorphic *ins)
 {
@@ -2651,17 +2652,19 @@ LIRGenerator::visitGetElementCache(MGetE
         JS_ASSERT(ins->index()->type() == MIRType_Value);
         LGetElementCacheV *lir = new LGetElementCacheV(useRegister(ins->object()));
         if (!useBox(lir, LGetElementCacheV::Index, ins->index()))
             return false;
         return defineBox(lir, ins) && assignSafepoint(lir, ins);
     }
 
     JS_ASSERT(ins->index()->type() == MIRType_Int32);
-    LGetElementCacheT *lir = newLGetElementCacheT(ins);
+    LGetElementCacheT *lir = new LGetElementCacheT(useRegister(ins->object()),
+                                                   useRegister(ins->index()),
+                                                   tempForDispatchCache(ins->type()));
     return define(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
 LIRGenerator::visitBindNameCache(MBindNameCache *ins)
 {
     JS_ASSERT(ins->scopeChain()->type() == MIRType_Object);
     JS_ASSERT(ins->type() == MIRType_Object);
@@ -2753,17 +2756,17 @@ LIRGenerator::visitCallGetElement(MCallG
     if (!defineReturn(lir, ins))
         return false;
     return assignSafepoint(lir, ins);
 }
 
 bool
 LIRGenerator::visitCallSetProperty(MCallSetProperty *ins)
 {
-    LInstruction *lir = new LCallSetProperty(useRegisterAtStart(ins->obj()));
+    LInstruction *lir = new LCallSetProperty(useRegisterAtStart(ins->object()));
     if (!useBoxAtStart(lir, LCallSetProperty::Value, ins->value()))
         return false;
     if (!add(lir, ins))
         return false;
     return assignSafepoint(lir, ins);
 }
 
 bool
@@ -2784,27 +2787,28 @@ LIRGenerator::visitDeleteElement(MDelete
     if(!useBoxAtStart(lir, LCallDeleteElement::Index, ins->index()))
         return false;
     return defineReturn(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
 LIRGenerator::visitSetPropertyCache(MSetPropertyCache *ins)
 {
-    LUse obj = useRegisterAtStart(ins->obj());
-    LDefinition slots = tempCopy(ins->obj(), 0);
+    LUse obj = useRegisterAtStart(ins->object());
+    LDefinition slots = tempCopy(ins->object(), 0);
+    LDefinition dispatchTemp = tempForDispatchCache();
 
     LInstruction *lir;
     if (ins->value()->type() == MIRType_Value) {
-        lir = new LSetPropertyCacheV(obj, slots);
+        lir = new LSetPropertyCacheV(obj, slots, dispatchTemp);
         if (!useBox(lir, LSetPropertyCacheV::Value, ins->value()))
             return false;
     } else {
         LAllocation value = useRegisterOrConstant(ins->value());
-        lir = new LSetPropertyCacheT(obj, slots, value, ins->value()->type());
+        lir = new LSetPropertyCacheT(obj, slots, value, dispatchTemp, ins->value()->type());
     }
 
     if (!add(lir, ins))
         return false;
 
     return assignSafepoint(lir, ins);
 }
 
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -6961,17 +6961,17 @@ class MSetPropertyInstruction : public M
   protected:
     MSetPropertyInstruction(MDefinition *obj, MDefinition *value, PropertyName *name,
                             bool strict)
       : MBinaryInstruction(obj, value),
         name_(name), strict_(strict), needsBarrier_(true)
     {}
 
   public:
-    MDefinition *obj() const {
+    MDefinition *object() const {
         return getOperand(0);
     }
     MDefinition *value() const {
         return getOperand(1);
     }
     PropertyName *name() const {
         return name_;
     }
--- a/js/src/jit/ParallelFunctions.cpp
+++ b/js/src/jit/ParallelFunctions.cpp
@@ -155,16 +155,40 @@ jit::ExtendArrayPar(ForkJoinSlice *slice
     JSObject::EnsureDenseResult res =
         array->ensureDenseElementsPreservePackedFlag(slice, 0, length);
     if (res != JSObject::ED_OK)
         return nullptr;
     return array;
 }
 
 ParallelResult
+jit::SetPropertyPar(ForkJoinSlice *slice, HandleObject obj, HandlePropertyName name,
+                    HandleValue value, bool strict, jsbytecode *pc)
+{
+    JS_ASSERT(slice->isThreadLocal(obj));
+
+    if (*pc == JSOP_SETALIASEDVAR) {
+        // See comment in jit::SetProperty.
+        Shape *shape = obj->nativeLookupPure(name);
+        JS_ASSERT(shape && shape->hasSlot());
+        return obj->nativeSetSlotIfHasType(shape, value) ? TP_SUCCESS : TP_RETRY_SEQUENTIALLY;
+    }
+
+    // Fail early on hooks.
+    if (obj->getOps()->setProperty)
+        return TP_RETRY_SEQUENTIALLY;
+
+    RootedValue v(slice, value);
+    RootedId id(slice, NameToId(name));
+    if (!baseops::SetPropertyHelper<ParallelExecution>(slice, obj, obj, id, 0, &v, strict))
+        return TP_RETRY_SEQUENTIALLY;
+    return TP_SUCCESS;
+}
+
+ParallelResult
 jit::SetElementPar(ForkJoinSlice *slice, HandleObject obj, HandleValue index, HandleValue value,
                    bool strict)
 {
     RootedId id(slice);
     if (!ValueToIdPure(index, id.address()))
         return TP_RETRY_SEQUENTIALLY;
 
     // SetObjectElementOperation, the sequential version, has several checks
--- a/js/src/jit/ParallelFunctions.h
+++ b/js/src/jit/ParallelFunctions.h
@@ -20,16 +20,18 @@ bool CheckOverRecursedPar(ForkJoinSlice 
 bool CheckInterruptPar(ForkJoinSlice *slice);
 
 // Extends the given array with `length` new holes.  Returns nullptr on
 // failure or else `array`, which is convenient during code
 // generation.
 JSObject *ExtendArrayPar(ForkJoinSlice *slice, JSObject *array, uint32_t length);
 
 // Set properties and elements on thread local objects.
+ParallelResult SetPropertyPar(ForkJoinSlice *slice, HandleObject obj, HandlePropertyName name,
+                              HandleValue value, bool strict, jsbytecode *pc);
 ParallelResult SetElementPar(ForkJoinSlice *slice, HandleObject obj, HandleValue index,
                              HandleValue value, bool strict);
 
 // String related parallel functions. These tend to call existing VM functions
 // that take a ThreadSafeContext.
 ParallelResult ConcatStringsPar(ForkJoinSlice *slice, HandleString left, HandleString right,
                                 MutableHandleString out);
 ParallelResult IntToStringPar(ForkJoinSlice *slice, int i, MutableHandleString out);
--- a/js/src/jit/ParallelSafetyAnalysis.cpp
+++ b/js/src/jit/ParallelSafetyAnalysis.cpp
@@ -233,20 +233,20 @@ class ParallelSafetyVisitor : public MIn
     WRITE_GUARDED_OP(StoreFixedSlot, object)
     UNSAFE_OP(CallGetProperty)
     UNSAFE_OP(GetNameCache)
     UNSAFE_OP(CallGetIntrinsicValue)
     UNSAFE_OP(CallsiteCloneCache)
     UNSAFE_OP(CallGetElement)
     UNSAFE_OP(CallSetElement)
     UNSAFE_OP(CallInitElementArray)
-    UNSAFE_OP(CallSetProperty)
+    WRITE_GUARDED_OP(CallSetProperty, object)
     UNSAFE_OP(DeleteProperty)
     UNSAFE_OP(DeleteElement)
-    UNSAFE_OP(SetPropertyCache)
+    WRITE_GUARDED_OP(SetPropertyCache, object)
     UNSAFE_OP(IteratorStart)
     UNSAFE_OP(IteratorNext)
     UNSAFE_OP(IteratorMore)
     UNSAFE_OP(IteratorEnd)
     SAFE_OP(StringLength)
     SAFE_OP(ArgumentsLength)
     SAFE_OP(GetFrameArgument)
     UNSAFE_OP(SetFrameArgument)
--- a/js/src/jit/arm/Lowering-arm.cpp
+++ b/js/src/jit/arm/Lowering-arm.cpp
@@ -359,30 +359,16 @@ LIRGeneratorARM::newLTableSwitch(const L
 }
 
 LTableSwitchV *
 LIRGeneratorARM::newLTableSwitchV(MTableSwitch *tableswitch)
 {
     return new LTableSwitchV(temp(), tempFloat(), tableswitch);
 }
 
-LGetPropertyCacheT *
-LIRGeneratorARM::newLGetPropertyCacheT(MGetPropertyCache *ins)
-{
-    return new LGetPropertyCacheT(useRegister(ins->object()), LDefinition::BogusTemp());
-}
-
-LGetElementCacheT *
-LIRGeneratorARM::newLGetElementCacheT(MGetElementCache *ins)
-{
-    return new LGetElementCacheT(useRegister(ins->object()),
-                                 useRegister(ins->index()),
-                                 LDefinition::BogusTemp());
-}
-
 bool
 LIRGeneratorARM::visitGuardShape(MGuardShape *ins)
 {
     JS_ASSERT(ins->obj()->type() == MIRType_Object);
 
     LDefinition tempObj = temp(LDefinition::OBJECT);
     LGuardShape *guard = new LGuardShape(useRegister(ins->obj()), tempObj);
     if (!assignSnapshot(guard, ins->bailoutKind()))
--- a/js/src/jit/arm/Lowering-arm.h
+++ b/js/src/jit/arm/Lowering-arm.h
@@ -30,16 +30,22 @@ class LIRGeneratorARM : public LIRGenera
     // stores and loads; on ARM all registers are okay.
     LAllocation useByteOpRegister(MDefinition *mir);
     LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition *mir);
 
     inline LDefinition tempToUnbox() {
         return LDefinition::BogusTemp();
     }
 
+    // x64 has a scratch register, so no need for another temp for dispatch
+    // ICs.
+    LDefinition tempForDispatchCache(MIRType outputType = MIRType_None) {
+        return LDefinition::BogusTemp();
+    }
+
     void lowerUntypedPhiInput(MPhi *phi, uint32_t inputPosition, LBlock *block, size_t lirIndex);
     bool defineUntypedPhi(MPhi *phi, size_t lirIndex);
     bool lowerForShift(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, MDefinition *lhs,
                        MDefinition *rhs);
     bool lowerUrshD(MUrsh *mir);
 
     bool lowerForALU(LInstructionHelper<1, 1, 0> *ins, MDefinition *mir,
                      MDefinition *input);
@@ -63,18 +69,16 @@ class LIRGeneratorARM : public LIRGenera
     bool visitPowHalf(MPowHalf *ins);
     bool visitAsmJSNeg(MAsmJSNeg *ins);
     bool visitAsmJSUDiv(MAsmJSUDiv *ins);
     bool visitAsmJSUMod(MAsmJSUMod *ins);
 
     LTableSwitch *newLTableSwitch(const LAllocation &in, const LDefinition &inputCopy,
                                   MTableSwitch *ins);
     LTableSwitchV *newLTableSwitchV(MTableSwitch *ins);
-    LGetPropertyCacheT *newLGetPropertyCacheT(MGetPropertyCache *ins);
-    LGetElementCacheT *newLGetElementCacheT(MGetElementCache *ins);
 
   public:
     bool visitConstant(MConstant *ins);
     bool visitBox(MBox *box);
     bool visitUnbox(MUnbox *unbox);
     bool visitReturn(MReturn *ret);
     bool lowerPhi(MPhi *phi);
     bool visitGuardShape(MGuardShape *ins);
--- a/js/src/jit/x64/Lowering-x64.cpp
+++ b/js/src/jit/x64/Lowering-x64.cpp
@@ -162,27 +162,13 @@ LIRGeneratorX64::visitAsmJSStoreHeap(MAs
 }
 
 bool
 LIRGeneratorX64::visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins)
 {
     return define(new LAsmJSLoadFuncPtr(useRegister(ins->index()), temp()), ins);
 }
 
-LGetPropertyCacheT *
-LIRGeneratorX64::newLGetPropertyCacheT(MGetPropertyCache *ins)
-{
-    return new LGetPropertyCacheT(useRegister(ins->object()), LDefinition::BogusTemp());
-}
-
-LGetElementCacheT *
-LIRGeneratorX64::newLGetElementCacheT(MGetElementCache *ins)
-{
-    return new LGetElementCacheT(useRegister(ins->object()),
-                                 useRegister(ins->index()),
-                                 LDefinition::BogusTemp());
-}
-
 bool
 LIRGeneratorX64::visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins)
 {
     MOZ_ASSUME_UNREACHABLE("NYI");
 }
--- a/js/src/jit/x64/Lowering-x64.h
+++ b/js/src/jit/x64/Lowering-x64.h
@@ -30,18 +30,21 @@ class LIRGeneratorX64 : public LIRGenera
 
     // x86 has constraints on what registers can be formatted for 1-byte
     // stores and loads; on x64 all registers are okay.
     LAllocation useByteOpRegister(MDefinition *mir);
     LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition *mir);
 
     LDefinition tempToUnbox();
 
-    LGetPropertyCacheT *newLGetPropertyCacheT(MGetPropertyCache *ins);
-    LGetElementCacheT *newLGetElementCacheT(MGetElementCache *ins);
+    // x64 has a scratch register, so no need for another temp for dispatch
+    // ICs.
+    LDefinition tempForDispatchCache(MIRType outputType = MIRType_None) {
+        return LDefinition::BogusTemp();
+    }
 
   public:
     bool visitBox(MBox *box);
     bool visitUnbox(MUnbox *unbox);
     bool visitReturn(MReturn *ret);
     bool visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins);
     bool visitAsmJSLoadHeap(MAsmJSLoadHeap *ins);
     bool visitAsmJSStoreHeap(MAsmJSStoreHeap *ins);
--- a/js/src/jit/x86/CodeGenerator-x86.cpp
+++ b/js/src/jit/x86/CodeGenerator-x86.cpp
@@ -742,16 +742,27 @@ GetElementParIC::initializeAddCacheState
     // one, it's BogusTemp otherwise.
     JS_ASSERT(ins->isGetElementCacheV() || ins->isGetElementCacheT());
     if (ins->isGetElementCacheV() || ins->toGetElementCacheT()->temp()->isBogusTemp())
         addState->dispatchScratch = output_.scratchReg().gpr();
     else
         addState->dispatchScratch = ToRegister(ins->toGetElementCacheT()->temp());
 }
 
+void
+SetPropertyParIC::initializeAddCacheState(LInstruction *ins, AddCacheState *addState)
+{
+    // We don't have an output register to reuse, so we always need a temp.
+    JS_ASSERT(ins->isSetPropertyCacheV() || ins->isSetPropertyCacheT());
+    if (ins->isSetPropertyCacheV())
+        addState->dispatchScratch = ToRegister(ins->toSetPropertyCacheV()->tempForDispatchCache());
+    else
+        addState->dispatchScratch = ToRegister(ins->toSetPropertyCacheT()->tempForDispatchCache());
+}
+
 namespace js {
 namespace jit {
 
 class OutOfLineTruncate : public OutOfLineCodeBase<CodeGeneratorX86>
 {
     LTruncateDToInt32 *ins_;
 
   public:
--- a/js/src/jit/x86/Lowering-x86.cpp
+++ b/js/src/jit/x86/Lowering-x86.cpp
@@ -9,16 +9,39 @@
 #include "jit/MIR.h"
 #include "jit/x86/Assembler-x86.h"
 
 #include "jit/shared/Lowering-shared-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
+LDefinition
+LIRGeneratorX86::tempForDispatchCache(MIRType outputType)
+{
+    // x86 doesn't have a scratch register and we need one for the
+    // indirect jump for dispatch-style ICs.
+    //
+    // Note that currently we only install dispatch-style ICs for parallel
+    // execution. If this assumption changes, please change it here.
+    if (gen->info().executionMode() != ParallelExecution)
+        return LDefinition::BogusTemp();
+
+    // If we don't have an output register, we need a temp.
+    if (outputType == MIRType_None)
+        return temp();
+
+    // If we have a double output register, we need a temp.
+    if (outputType == MIRType_Double)
+        return temp();
+
+    // Otherwise we have a non-double output register and we can reuse it.
+    return LDefinition::BogusTemp();
+}
+
 bool
 LIRGeneratorX86::useBox(LInstruction *lir, size_t n, MDefinition *mir,
                         LUse::Policy policy, bool useAtStart)
 {
     JS_ASSERT(mir->type() == MIRType_Value);
 
     if (!ensureDefined(mir))
         return false;
@@ -270,35 +293,8 @@ LIRGeneratorX86::visitStoreTypedArrayEle
     return add(lir, ins);
 }
 
 bool
 LIRGeneratorX86::visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins)
 {
     return define(new LAsmJSLoadFuncPtr(useRegisterAtStart(ins->index())), ins);
 }
-
-LGetPropertyCacheT *
-LIRGeneratorX86::newLGetPropertyCacheT(MGetPropertyCache *ins)
-{
-    // Since x86 doesn't have a scratch register and we need one for the
-    // indirect jump for dispatch-style ICs, we need a temporary in the case
-    // of a double output type as we can't get a scratch from the output.
-    LDefinition scratch;
-    if (ins->type() == MIRType_Double)
-        scratch = temp();
-    else
-        scratch = LDefinition::BogusTemp();
-    return new LGetPropertyCacheT(useRegister(ins->object()), scratch);
-}
-
-LGetElementCacheT *
-LIRGeneratorX86::newLGetElementCacheT(MGetElementCache *ins)
-{
-    LDefinition scratch;
-    if (ins->type() == MIRType_Double)
-        scratch = temp();
-    else
-        scratch = LDefinition::BogusTemp();
-    return new LGetElementCacheT(useRegister(ins->object()),
-                                 useRegister(ins->index()),
-                                 scratch);
-}
--- a/js/src/jit/x86/Lowering-x86.h
+++ b/js/src/jit/x86/Lowering-x86.h
@@ -33,22 +33,21 @@ class LIRGeneratorX86 : public LIRGenera
     // give us one of {al,bl,cl,dl}. For now, just useFixed(al).
     LAllocation useByteOpRegister(MDefinition *mir);
     LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition *mir);
 
     inline LDefinition tempToUnbox() {
         return LDefinition::BogusTemp();
     }
 
+    LDefinition tempForDispatchCache(MIRType outputType = MIRType_None);
+
     void lowerUntypedPhiInput(MPhi *phi, uint32_t inputPosition, LBlock *block, size_t lirIndex);
     bool defineUntypedPhi(MPhi *phi, size_t lirIndex);
 
-    LGetPropertyCacheT *newLGetPropertyCacheT(MGetPropertyCache *ins);
-    LGetElementCacheT *newLGetElementCacheT(MGetElementCache *ins);
-
   public:
     bool visitBox(MBox *box);
     bool visitUnbox(MUnbox *unbox);
     bool visitReturn(MReturn *ret);
     bool visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins);
     bool visitAsmJSLoadHeap(MAsmJSLoadHeap *ins);
     bool visitAsmJSStoreHeap(MAsmJSStoreHeap *ins);
     bool visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins);