Bug 1326067 part 3 - Port Baseline unboxed object SetProp stub to CacheIR. r=evilpie
authorJan de Mooij <jdemooij@mozilla.com>
Thu, 19 Jan 2017 14:21:16 +0100
changeset 375114 d5955f3c7143d00e95c40ee6a533d13090369f7c
parent 375113 3bb7c631c0d6888dda43c8eafa5616991d5a2cb8
child 375115 1d62adf74d8965586e6a5d270ff40a8967f87ee8
push id6996
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 20:48:21 +0000
treeherdermozilla-beta@d89512dab048 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersevilpie
bugs1326067
milestone53.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 1326067 part 3 - Port Baseline unboxed object SetProp stub to CacheIR. r=evilpie
js/src/jit/BaselineCacheIRCompiler.cpp
js/src/jit/BaselineIC.cpp
js/src/jit/BaselineIC.h
js/src/jit/BaselineICList.h
js/src/jit/BaselineInspector.cpp
js/src/jit/CacheIR.cpp
js/src/jit/CacheIR.h
js/src/jit/CacheIRCompiler.cpp
js/src/jit/CacheIRCompiler.h
js/src/jit/IonCacheIRCompiler.cpp
js/src/jit/SharedIC.cpp
--- a/js/src/jit/BaselineCacheIRCompiler.cpp
+++ b/js/src/jit/BaselineCacheIRCompiler.cpp
@@ -761,16 +761,55 @@ BaselineCacheIRCompiler::emitStoreFixedS
 
 bool
 BaselineCacheIRCompiler::emitStoreDynamicSlot()
 {
     return emitStoreSlotShared(false);
 }
 
 bool
+BaselineCacheIRCompiler::emitStoreUnboxedProperty()
+{
+    ObjOperandId objId = reader.objOperandId();
+    JSValueType fieldType = reader.valueType();
+    Address offsetAddr = stubAddress(reader.stubOffset());
+
+    // Allocate the fixed registers first. These need to be fixed for
+    // callTypeUpdateIC.
+    AutoStubFrame stubFrame(*this);
+    AutoScratchRegister scratch(allocator, masm, R1.scratchReg());
+    ValueOperand val = allocator.useFixedValueRegister(masm, reader.valOperandId(), R0);
+
+    Register obj = allocator.useRegister(masm, objId);
+
+    // We only need the type update IC if we are storing an object.
+    if (fieldType == JSVAL_TYPE_OBJECT) {
+        LiveGeneralRegisterSet saveRegs;
+        saveRegs.add(obj);
+        saveRegs.add(val);
+        if (!callTypeUpdateIC(stubFrame, obj, val, scratch, saveRegs))
+            return false;
+    }
+
+    masm.load32(offsetAddr, scratch);
+    BaseIndex fieldAddr(obj, scratch, TimesOne);
+
+    // Note that the storeUnboxedProperty call here is infallible, as the
+    // IR emitter is responsible for guarding on |val|'s type.
+    EmitUnboxedPreBarrierForBaseline(masm, fieldAddr, fieldType);
+    masm.storeUnboxedProperty(fieldAddr, fieldType,
+                              ConstantOrRegister(TypedOrValueRegister(val)),
+                              /* failure = */ nullptr);
+
+    if (UnboxedTypeNeedsPostBarrier(fieldType))
+        BaselineEmitPostWriteBarrierSlot(masm, obj, val, scratch, LiveGeneralRegisterSet(), cx_);
+    return true;
+}
+
+bool
 BaselineCacheIRCompiler::emitTypeMonitorResult()
 {
     allocator.discardStack(masm);
     EmitEnterTypeMonitorIC(masm);
     return true;
 }
 
 bool
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -287,18 +287,17 @@ DoTypeUpdateFallback(JSContext* cx, Base
         AddTypePropertyId(cx, obj, id, value);
         break;
       case ICStub::SetElem_DenseOrUnboxedArray:
       case ICStub::SetElem_DenseOrUnboxedArrayAdd: {
         id = JSID_VOID;
         AddTypePropertyId(cx, obj, id, value);
         break;
       }
-      case ICStub::SetProp_NativeAdd:
-      case ICStub::SetProp_Unboxed: {
+      case ICStub::SetProp_NativeAdd: {
         MOZ_ASSERT(obj->isNative() || obj->is<UnboxedPlainObject>());
         jsbytecode* pc = stub->getChainFallback()->icEntry()->pc(script);
         if (*pc == JSOP_SETALIASEDVAR || *pc == JSOP_INITALIASEDLEXICAL)
             id = NameToId(EnvironmentCoordinateName(cx->caches.envCoordinateNameCache, script, pc));
         else
             id = NameToId(script->getName(pc));
         AddTypePropertyId(cx, obj, id, value);
         break;
@@ -1447,19 +1446,18 @@ BaselineScript::noteArrayWriteHole(uint3
     if (stub->isSetElem_Fallback())
         stub->toSetElem_Fallback()->noteArrayWriteHole();
 }
 
 //
 // SetElem_DenseOrUnboxedArray
 //
 
-template <typename T>
 void
-EmitUnboxedPreBarrierForBaseline(MacroAssembler &masm, T address, JSValueType type)
+EmitUnboxedPreBarrierForBaseline(MacroAssembler &masm, const BaseIndex& address, JSValueType type)
 {
     if (type == JSVAL_TYPE_OBJECT)
         EmitPreBarrier(masm, address, MIRType::Object);
     else if (type == JSVAL_TYPE_STRING)
         EmitPreBarrier(masm, address, MIRType::String);
     else
         MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(type));
 }
@@ -2746,50 +2744,16 @@ TryAttachSetAccessorPropStub(JSContext* 
         *attached = true;
         return true;
     }
 
     return true;
 }
 
 static bool
-TryAttachUnboxedSetPropStub(JSContext* cx, HandleScript script,
-                            ICSetProp_Fallback* stub, HandleId id,
-                            HandleObject obj, HandleValue rhs, bool* attached)
-{
-    MOZ_ASSERT(!*attached);
-
-    if (!cx->runtime()->jitSupportsFloatingPoint)
-        return true;
-
-    if (!obj->is<UnboxedPlainObject>())
-        return true;
-
-    const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(id);
-    if (!property)
-        return true;
-
-    ICSetProp_Unboxed::Compiler compiler(cx, obj->group(),
-                                         property->offset + UnboxedPlainObject::offsetOfData(),
-                                         property->type);
-    ICUpdatedStub* newStub = compiler.getStub(compiler.getStubSpace(script));
-    if (!newStub)
-        return false;
-    if (compiler.needsUpdateStubs() && !newStub->addUpdateStubForValue(cx, script, obj, id, rhs))
-        return false;
-
-    stub->addNewStub(newStub);
-
-    StripPreliminaryObjectStubs(cx, stub);
-
-    *attached = true;
-    return true;
-}
-
-static bool
 TryAttachTypedObjectSetPropStub(JSContext* cx, HandleScript script,
                                 ICSetProp_Fallback* stub, HandleId id,
                                 HandleObject obj, HandleValue rhs, bool* attached)
 {
     MOZ_ASSERT(!*attached);
 
     if (!cx->runtime()->jitSupportsFloatingPoint)
         return true;
@@ -2965,25 +2929,16 @@ DoSetPropFallback(JSContext* cx, Baselin
     {
         return false;
     }
     if (attached)
         return true;
 
     if (!attached &&
         lhs.isObject() &&
-        !TryAttachUnboxedSetPropStub(cx, script, stub, id, obj, rhs, &attached))
-    {
-        return false;
-    }
-    if (attached)
-        return true;
-
-    if (!attached &&
-        lhs.isObject() &&
         !TryAttachTypedObjectSetPropStub(cx, script, stub, id, obj, rhs, &attached))
     {
         return false;
     }
     if (attached)
         return true;
 
     MOZ_ASSERT(!attached);
@@ -3238,77 +3193,16 @@ ICSetPropNativeAddCompiler::generateStub
     EmitUnstowICValues(masm, 2);
 
     masm.bind(&failure);
     EmitStubGuardFailure(masm);
     return true;
 }
 
 bool
-ICSetProp_Unboxed::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    MOZ_ASSERT(engine_ == Engine::Baseline);
-
-    Label failure;
-
-    // Guard input is an object.
-    masm.branchTestObject(Assembler::NotEqual, R0, &failure);
-
-    AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
-    Register scratch = regs.takeAny();
-
-    // Unbox and group guard.
-    Register object = masm.extractObject(R0, ExtractTemp0);
-    masm.loadPtr(Address(ICStubReg, ICSetProp_Unboxed::offsetOfGroup()), scratch);
-    masm.branchPtr(Assembler::NotEqual, Address(object, JSObject::offsetOfGroup()), scratch,
-                   &failure);
-
-    if (needsUpdateStubs()) {
-        // Stow both R0 and R1 (object and value).
-        EmitStowICValues(masm, 2);
-
-        // Move RHS into R0 for TypeUpdate check.
-        masm.moveValue(R1, R0);
-
-        // Call the type update stub.
-        if (!callTypeUpdateIC(masm, sizeof(Value)))
-            return false;
-
-        // Unstow R0 and R1 (object and key)
-        EmitUnstowICValues(masm, 2);
-
-        // The TypeUpdate IC may have smashed object. Rederive it.
-        masm.unboxObject(R0, object);
-
-        // Trigger post barriers here on the values being written. Fields which
-        // objects can be written to also need update stubs.
-        LiveGeneralRegisterSet saveRegs;
-        saveRegs.add(R0);
-        saveRegs.add(R1);
-        saveRegs.addUnchecked(object);
-        saveRegs.add(ICStubReg);
-        BaselineEmitPostWriteBarrierSlot(masm, object, R1, scratch, saveRegs, cx);
-    }
-
-    // Compute the address being written to.
-    masm.load32(Address(ICStubReg, ICSetProp_Unboxed::offsetOfFieldOffset()), scratch);
-    BaseIndex address(object, scratch, TimesOne);
-
-    EmitUnboxedPreBarrierForBaseline(masm, address, fieldType_);
-    masm.storeUnboxedProperty(address, fieldType_,
-                              ConstantOrRegister(TypedOrValueRegister(R1)), &failure);
-
-    EmitReturnFromIC(masm);
-
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-    return true;
-}
-
-bool
 ICSetProp_TypedObject::Compiler::generateStubCode(MacroAssembler& masm)
 {
     MOZ_ASSERT(engine_ == Engine::Baseline);
 
     Label failure;
 
     CheckForTypedObjectWithDetachedStorage(cx, masm, &failure);
 
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -1230,80 +1230,16 @@ class ICSetPropNativeAddCompiler : publi
 
         return newStub<ICSetProp_NativeAddImpl<ProtoChainDepth>>(
             space, getStubCode(), oldGroup_, shapes, newShape, newGroup, offset_);
     }
 
     ICUpdatedStub* getStub(ICStubSpace* space);
 };
 
-class ICSetProp_Unboxed : public ICUpdatedStub
-{
-    friend class ICStubSpace;
-
-    GCPtrObjectGroup group_;
-    uint32_t fieldOffset_;
-
-    ICSetProp_Unboxed(JitCode* stubCode, ObjectGroup* group, uint32_t fieldOffset)
-      : ICUpdatedStub(ICStub::SetProp_Unboxed, stubCode),
-        group_(group),
-        fieldOffset_(fieldOffset)
-    {
-        (void) fieldOffset_; // Silence clang warning
-    }
-
-  public:
-    GCPtrObjectGroup& group() {
-        return group_;
-    }
-
-    static size_t offsetOfGroup() {
-        return offsetof(ICSetProp_Unboxed, group_);
-    }
-    static size_t offsetOfFieldOffset() {
-        return offsetof(ICSetProp_Unboxed, fieldOffset_);
-    }
-
-    class Compiler : public ICStubCompiler {
-      protected:
-        RootedObjectGroup group_;
-        uint32_t fieldOffset_;
-        JSValueType fieldType_;
-
-        MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
-
-        virtual int32_t getKey() const {
-            return static_cast<int32_t>(engine_) |
-                  (static_cast<int32_t>(kind) << 1) |
-                  (static_cast<int32_t>(fieldType_) << 17);
-        }
-
-      public:
-        Compiler(JSContext* cx, ObjectGroup* group, uint32_t fieldOffset,
-                 JSValueType fieldType)
-          : ICStubCompiler(cx, ICStub::SetProp_Unboxed, Engine::Baseline),
-            group_(cx, group),
-            fieldOffset_(fieldOffset),
-            fieldType_(fieldType)
-        {}
-
-        ICUpdatedStub* getStub(ICStubSpace* space) {
-            ICUpdatedStub* stub = newStub<ICSetProp_Unboxed>(space, getStubCode(), group_,
-                                                             fieldOffset_);
-            if (!stub || !stub->initUpdatingChain(cx, space))
-                return nullptr;
-            return stub;
-        }
-
-        bool needsUpdateStubs() {
-            return fieldType_ == JSVAL_TYPE_OBJECT;
-        }
-    };
-};
-
 class ICSetProp_TypedObject : public ICUpdatedStub
 {
     friend class ICStubSpace;
 
     GCPtrShape shape_;
     GCPtrObjectGroup group_;
     uint32_t fieldOffset_;
     bool isObjectReference_;
@@ -2590,12 +2526,15 @@ IsCacheableDOMProxy(JSObject* obj)
         return false;
 
     const BaseProxyHandler* handler = obj->as<ProxyObject>().handler();
     return handler->family() == GetDOMProxyHandlerFamily();
 }
 
 struct IonOsrTempData;
 
+void EmitUnboxedPreBarrierForBaseline(MacroAssembler &masm, const BaseIndex& address,
+                                      JSValueType type);
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_BaselineIC_h */
--- a/js/src/jit/BaselineICList.h
+++ b/js/src/jit/BaselineICList.h
@@ -65,17 +65,16 @@ namespace jit {
                                                  \
     _(BindName_Fallback)                         \
                                                  \
     _(GetIntrinsic_Fallback)                     \
     _(GetIntrinsic_Constant)                     \
                                                  \
     _(SetProp_Fallback)                          \
     _(SetProp_NativeAdd)                         \
-    _(SetProp_Unboxed)                           \
     _(SetProp_TypedObject)                       \
     _(SetProp_CallScripted)                      \
     _(SetProp_CallNative)                        \
                                                  \
     _(TableSwitch)                               \
                                                  \
     _(IteratorNew_Fallback)                      \
     _(IteratorMore_Fallback)                     \
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -209,16 +209,52 @@ GetCacheIRReceiverForNativeSetSlot(ICCac
 
     if (!reader.matchOpEither(CacheOp::StoreFixedSlot, CacheOp::StoreDynamicSlot))
         return false;
 
     *receiver = ReceiverGuard(group, shape);
     return true;
 }
 
+static bool
+GetCacheIRReceiverForUnboxedProperty(ICCacheIR_Updated* stub, ReceiverGuard* receiver)
+{
+    // We match:
+    //
+    //   GuardIsObject 0
+    //   GuardGroup 0
+    //   GuardType 1 type | GuardIsObjectOrNull 1
+    //   StoreUnboxedProperty 0
+
+    *receiver = ReceiverGuard();
+    CacheIRReader reader(stub->stubInfo());
+
+    ObjOperandId objId = ObjOperandId(0);
+    ValOperandId rhsId = ValOperandId(1);
+    if (!reader.matchOp(CacheOp::GuardIsObject, objId))
+        return false;
+
+    if (!reader.matchOp(CacheOp::GuardGroup, objId))
+        return false;
+    ObjectGroup* group = stub->stubInfo()->getStubField<ObjectGroup*>(stub, reader.stubOffset());
+
+    if (reader.matchOp(CacheOp::GuardType, rhsId)) {
+        reader.valueType(); // Skip.
+    } else {
+        if (!reader.matchOp(CacheOp::GuardIsObjectOrNull, rhsId))
+            return false;
+    }
+
+    if (!reader.matchOp(CacheOp::StoreUnboxedProperty))
+        return false;
+
+    *receiver = ReceiverGuard(group, nullptr);
+    return true;
+}
+
 bool
 BaselineInspector::maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receivers,
                                           ObjectGroupVector& convertUnboxedGroups)
 {
     // Return a list of the receivers seen by the baseline IC for the current
     // op. Empty lists indicate no receivers are known, or there was an
     // uncacheable access. convertUnboxedGroups is used for unboxed object
     // groups which have been seen, but have had instances converted to native
@@ -238,22 +274,22 @@ BaselineInspector::maybeInfoForPropertyO
         if (stub->isCacheIR_Monitored()) {
             if (!GetCacheIRReceiverForNativeReadSlot(stub->toCacheIR_Monitored(), &receiver) &&
                 !GetCacheIRReceiverForUnboxedProperty(stub->toCacheIR_Monitored(), &receiver))
             {
                 receivers.clear();
                 return true;
             }
         } else if (stub->isCacheIR_Updated()) {
-            if (!GetCacheIRReceiverForNativeSetSlot(stub->toCacheIR_Updated(), &receiver)) {
+            if (!GetCacheIRReceiverForNativeSetSlot(stub->toCacheIR_Updated(), &receiver) &&
+                !GetCacheIRReceiverForUnboxedProperty(stub->toCacheIR_Updated(), &receiver))
+            {
                 receivers.clear();
                 return true;
             }
-        } else if (stub->isSetProp_Unboxed()) {
-            receiver = ReceiverGuard(stub->toSetProp_Unboxed()->group(), nullptr);
         } else {
             receivers.clear();
             return true;
         }
 
         if (!AddReceiver(receiver, receivers, convertUnboxedGroups))
             return false;
 
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -1560,16 +1560,18 @@ SetPropIRGenerator::tryAttachStub()
             return false;
 
         ObjOperandId objId = writer.guardIsObject(lhsValId);
         if (nameOrSymbol) {
             if (tryAttachNativeSetSlot(obj, objId, id, rhsValId))
                 return true;
             if (tryAttachUnboxedExpandoSetSlot(obj, objId, id, rhsValId))
                 return true;
+            if (tryAttachUnboxedProperty(obj, objId, id, rhsValId))
+                return true;
         }
         return false;
     }
 
     return false;
 }
 
 static void
@@ -1658,8 +1660,42 @@ SetPropIRGenerator::tryAttachUnboxedExpa
     writer.guardGroup(objId, obj->group());
     ObjOperandId expandoId = writer.guardAndLoadUnboxedExpando(objId);
     writer.guardShape(expandoId, expando->lastProperty());
 
     setUpdateStubInfo(id);
     EmitStoreSlotAndReturn(writer, expandoId, expando, propShape, rhsId);
     return true;
 }
+
+static void
+EmitGuardUnboxedPropertyType(CacheIRWriter& writer, JSValueType propType, ValOperandId valId)
+{
+    if (propType == JSVAL_TYPE_OBJECT) {
+        // Unboxed objects store NullValue as nullptr object.
+        writer.guardIsObjectOrNull(valId);
+    } else {
+        writer.guardType(valId, propType);
+    }
+}
+
+bool
+SetPropIRGenerator::tryAttachUnboxedProperty(HandleObject obj, ObjOperandId objId, HandleId id,
+                                             ValOperandId rhsId)
+{
+    if (!obj->is<UnboxedPlainObject>() || !cx_->runtime()->jitSupportsFloatingPoint)
+        return false;
+
+    const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(id);
+    if (!property)
+        return false;
+
+    writer.guardGroup(objId, obj->group());
+    EmitGuardUnboxedPropertyType(writer, property->type, rhsId);
+    writer.storeUnboxedProperty(objId, property->type,
+                                UnboxedPlainObject::offsetOfData() + property->offset,
+                                rhsId);
+    writer.returnFromIC();
+
+    setUpdateStubInfo(id);
+    preliminaryObjectAction_ = PreliminaryObjectAction::Unlink;
+    return true;
+}
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -134,16 +134,17 @@ enum class CacheKind : uint8_t
     GetProp,
     GetElem,
     GetName,
     SetProp,
 };
 
 #define CACHE_IR_OPS(_)                   \
     _(GuardIsObject)                      \
+    _(GuardIsObjectOrNull)                \
     _(GuardIsString)                      \
     _(GuardIsSymbol)                      \
     _(GuardIsInt32Index)                  \
     _(GuardType)                          \
     _(GuardShape)                         \
     _(GuardGroup)                         \
     _(GuardProto)                         \
     _(GuardClass)                         \
@@ -166,16 +167,17 @@ enum class CacheKind : uint8_t
     /* See CacheIR.cpp 'DOM proxies' comment. */ \
     _(LoadDOMExpandoValue)                \
     _(LoadDOMExpandoValueGuardGeneration) \
     _(LoadDOMExpandoValueIgnoreGeneration)\
     _(GuardDOMExpandoMissingOrGuardShape) \
                                           \
     _(StoreFixedSlot)                     \
     _(StoreDynamicSlot)                   \
+    _(StoreUnboxedProperty)               \
                                           \
     /* The *Result ops load a value into the cache's result register. */ \
     _(LoadFixedSlotResult)                \
     _(LoadDynamicSlotResult)              \
     _(LoadUnboxedPropertyResult)          \
     _(LoadTypedObjectResult)              \
     _(LoadDenseElementResult)             \
     _(LoadDenseElementHoleResult)         \
@@ -424,16 +426,19 @@ class MOZ_RAII CacheIRWriter : public JS
         writeOperandId(res);
         return res;
     }
     void guardType(ValOperandId val, JSValueType type) {
         writeOpWithOperandId(CacheOp::GuardType, val);
         static_assert(sizeof(type) == sizeof(uint8_t), "JSValueType should fit in a byte");
         buffer_.writeByte(uint32_t(type));
     }
+    void guardIsObjectOrNull(ValOperandId val) {
+        writeOpWithOperandId(CacheOp::GuardIsObjectOrNull, val);
+    }
     void guardShape(ObjOperandId obj, Shape* shape) {
         writeOpWithOperandId(CacheOp::GuardShape, obj);
         addStubField(uintptr_t(shape), StubField::Type::Shape);
     }
     void guardGroup(ObjOperandId obj, ObjectGroup* group) {
         writeOpWithOperandId(CacheOp::GuardGroup, obj);
         addStubField(uintptr_t(group), StubField::Type::ObjectGroup);
     }
@@ -557,16 +562,24 @@ class MOZ_RAII CacheIRWriter : public JS
         addStubField(offset, StubField::Type::RawWord);
         writeOperandId(rhs);
     }
     void storeDynamicSlot(ObjOperandId obj, size_t offset, ValOperandId rhs) {
         writeOpWithOperandId(CacheOp::StoreDynamicSlot, obj);
         addStubField(offset, StubField::Type::RawWord);
         writeOperandId(rhs);
     }
+    void storeUnboxedProperty(ObjOperandId obj, JSValueType type, size_t offset,
+                              ValOperandId rhs)
+    {
+        writeOpWithOperandId(CacheOp::StoreUnboxedProperty, obj);
+        buffer_.writeByte(uint32_t(type));
+        addStubField(offset, StubField::Type::RawWord);
+        writeOperandId(rhs);
+    }
 
     void loadUndefinedResult() {
         writeOp(CacheOp::LoadUndefinedResult);
     }
     void loadFixedSlotResult(ObjOperandId obj, size_t offset) {
         writeOpWithOperandId(CacheOp::LoadFixedSlotResult, obj);
         addStubField(offset, StubField::Type::RawWord);
     }
@@ -860,16 +873,18 @@ class MOZ_RAII SetPropIRGenerator : publ
         needUpdateStub_ = true;
         updateStubId_ = id;
     }
 
     bool tryAttachNativeSetSlot(HandleObject obj, ObjOperandId objId, HandleId id,
                                  ValOperandId rhsId);
     bool tryAttachUnboxedExpandoSetSlot(HandleObject obj, ObjOperandId objId, HandleId id,
                                         ValOperandId rhsId);
+    bool tryAttachUnboxedProperty(HandleObject obj, ObjOperandId objId, HandleId id,
+                                  ValOperandId rhsId);
 
   public:
     SetPropIRGenerator(JSContext* cx, jsbytecode* pc, CacheKind cacheKind,
                        bool* isTemporarilyUnoptimizable, HandleValue lhsVal, HandleValue idVal,
                        HandleValue rhsVal);
 
     bool tryAttachStub();
 
--- a/js/src/jit/CacheIRCompiler.cpp
+++ b/js/src/jit/CacheIRCompiler.cpp
@@ -1052,16 +1052,36 @@ CacheIRCompiler::emitGuardIsObject()
     FailurePath* failure;
     if (!addFailurePath(&failure))
         return false;
     masm.branchTestObject(Assembler::NotEqual, input, failure->label());
     return true;
 }
 
 bool
+CacheIRCompiler::emitGuardIsObjectOrNull()
+{
+    ValOperandId inputId = reader.valOperandId();
+    JSValueType knownType = allocator.knownType(inputId);
+    if (knownType == JSVAL_TYPE_OBJECT || knownType == JSVAL_TYPE_NULL)
+        return true;
+
+    ValueOperand input = allocator.useValueRegister(masm, inputId);
+    FailurePath* failure;
+    if (!addFailurePath(&failure))
+        return false;
+
+    Label done;
+    masm.branchTestObject(Assembler::Equal, input, &done);
+    masm.branchTestNull(Assembler::NotEqual, input, failure->label());
+    masm.bind(&done);
+    return true;
+}
+
+bool
 CacheIRCompiler::emitGuardIsString()
 {
     ValOperandId inputId = reader.valOperandId();
     if (allocator.knownType(inputId) == JSVAL_TYPE_STRING)
         return true;
 
     ValueOperand input = allocator.useValueRegister(masm, inputId);
     FailurePath* failure;
@@ -1155,16 +1175,19 @@ CacheIRCompiler::emitGuardType()
 
     switch (type) {
       case JSVAL_TYPE_STRING:
         masm.branchTestString(Assembler::NotEqual, input, failure->label());
         break;
       case JSVAL_TYPE_SYMBOL:
         masm.branchTestSymbol(Assembler::NotEqual, input, failure->label());
         break;
+      case JSVAL_TYPE_INT32:
+        masm.branchTestInt32(Assembler::NotEqual, input, failure->label());
+        break;
       case JSVAL_TYPE_DOUBLE:
         masm.branchTestNumber(Assembler::NotEqual, input, failure->label());
         break;
       case JSVAL_TYPE_BOOLEAN:
         masm.branchTestBoolean(Assembler::NotEqual, input, failure->label());
         break;
       case JSVAL_TYPE_UNDEFINED:
         masm.branchTestUndefined(Assembler::NotEqual, input, failure->label());
--- a/js/src/jit/CacheIRCompiler.h
+++ b/js/src/jit/CacheIRCompiler.h
@@ -11,16 +11,17 @@
 
 namespace js {
 namespace jit {
 
 // The ops below are defined in CacheIRCompiler and codegen is shared between
 // BaselineCacheIRCompiler and IonCacheIRCompiler.
 #define CACHE_IR_SHARED_OPS(_)            \
     _(GuardIsObject)                      \
+    _(GuardIsObjectOrNull)                \
     _(GuardIsString)                      \
     _(GuardIsSymbol)                      \
     _(GuardIsInt32Index)                  \
     _(GuardType)                          \
     _(GuardClass)                         \
     _(GuardIsProxy)                       \
     _(GuardNotDOMProxy)                   \
     _(GuardMagicValue)                    \
--- a/js/src/jit/IonCacheIRCompiler.cpp
+++ b/js/src/jit/IonCacheIRCompiler.cpp
@@ -845,16 +845,22 @@ IonCacheIRCompiler::emitStoreFixedSlot()
 
 bool
 IonCacheIRCompiler::emitStoreDynamicSlot()
 {
     MOZ_CRASH("Baseline-specific op");
 }
 
 bool
+IonCacheIRCompiler::emitStoreUnboxedProperty()
+{
+    MOZ_CRASH("Baseline-specific op");
+}
+
+bool
 IonCacheIRCompiler::emitLoadTypedObjectResult()
 {
     AutoOutputRegister output(*this);
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     AutoScratchRegister scratch1(allocator, masm);
     AutoScratchRegister scratch2(allocator, masm);
 
     TypedThingLayout layout = reader.typedThingLayout();
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -367,21 +367,16 @@ ICStub::trace(JSTracer* trc)
           case 1: propStub->toImpl<1>()->traceShapes(trc); break;
           case 2: propStub->toImpl<2>()->traceShapes(trc); break;
           case 3: propStub->toImpl<3>()->traceShapes(trc); break;
           case 4: propStub->toImpl<4>()->traceShapes(trc); break;
           default: MOZ_CRASH("Invalid proto stub.");
         }
         break;
       }
-      case ICStub::SetProp_Unboxed: {
-        ICSetProp_Unboxed* propStub = toSetProp_Unboxed();
-        TraceEdge(trc, &propStub->group(), "baseline-setprop-unboxed-stub-group");
-        break;
-      }
       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();