Merge mozilla-central to autoland
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 27 Feb 2017 14:14:34 +0100
changeset 374037 bd53f4115df69503d4b47b04e4f030e87679923b
parent 374036 0260913ef653610e3b5183c037512b99423cd898 (current diff)
parent 374031 ecf39ae188da37206b39a31e585fdcb028bd8b7b (diff)
child 374038 d0cb52dd5feb8b49ec9f08006a7da6c499bf7d73
push id10863
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 23:02:23 +0000
treeherdermozilla-aurora@0931190cd725 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone54.0a1
Merge mozilla-central to autoland
--- a/devtools/client/inspector/inspector-search.js
+++ b/devtools/client/inspector/inspector-search.js
@@ -325,17 +325,16 @@ SelectorAutocompleter.prototype = {
     this.panelDoc = null;
   },
 
   /**
    * Handles keypresses inside the input box.
    */
   _onSearchKeypress: function (event) {
     let popup = this.searchPopup;
-
     switch (event.keyCode) {
       case KeyCodes.DOM_VK_RETURN:
       case KeyCodes.DOM_VK_TAB:
         if (popup.isOpen) {
           if (popup.selectedItem) {
             this.searchBox.value = popup.selectedItem.label;
           }
           this.hidePopup();
@@ -368,16 +367,19 @@ SelectorAutocompleter.prototype = {
           }
           this.searchBox.value = popup.selectedItem.label;
         }
         break;
 
       case KeyCodes.DOM_VK_ESCAPE:
         if (popup.isOpen) {
           this.hidePopup();
+        } else {
+          this.emit("processing-done");
+          return;
         }
         break;
 
       default:
         return;
     }
 
     event.preventDefault();
--- a/gfx/layers/client/ClientPaintedLayer.cpp
+++ b/gfx/layers/client/ClientPaintedLayer.cpp
@@ -144,17 +144,23 @@ ClientLayerManager::CreatePaintedLayer()
 {
   return CreatePaintedLayerWithHint(NONE);
 }
 
 already_AddRefed<PaintedLayer>
 ClientLayerManager::CreatePaintedLayerWithHint(PaintedLayerCreationHint aHint)
 {
   NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+  // The non-tiling ContentClient requires CrossProcessSemaphore which
+  // isn't implemented for OSX.
+#ifdef XP_MACOSX
+  if (true) {
+#else
   if (gfxPrefs::LayersTilesEnabled()) {
+#endif
     RefPtr<ClientTiledPaintedLayer> layer = new ClientTiledPaintedLayer(this, aHint);
     CREATE_SHADOW(Painted);
     return layer.forget();
   } else {
     RefPtr<ClientPaintedLayer> layer = new ClientPaintedLayer(this, aHint);
     CREATE_SHADOW(Painted);
     return layer.forget();
   }
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -1838,17 +1838,17 @@ CompositorBridgeParent::NotifyDidComposi
     mWrBridge->ExtractImageCompositeNotifications(&notifications);
     if (!notifications.IsEmpty()) {
       Unused << ImageBridgeParent::NotifyImageComposites(notifications);
     }
   }
 
   MonitorAutoLock lock(*sIndirectLayerTreesLock);
   ForEachIndirectLayerTree([&] (LayerTreeState* lts, const uint64_t& aLayersId) -> void {
-    if (lts->mCrossProcessParent) {
+    if (lts->mCrossProcessParent && lts->mParent == this) {
       CrossProcessCompositorBridgeParent* cpcp = lts->mCrossProcessParent;
       cpcp->DidComposite(aLayersId, aCompositeStart, aCompositeEnd);
     }
   });
 }
 
 void
 CompositorBridgeParent::InvalidateRemoteLayers()
--- a/js/src/jit/BaselineCacheIRCompiler.cpp
+++ b/js/src/jit/BaselineCacheIRCompiler.cpp
@@ -925,17 +925,17 @@ BaselineCacheIRCompiler::emitStoreUnboxe
             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);
+    EmitICUnboxedPreBarrier(masm, fieldAddr, fieldType);
     masm.storeUnboxedProperty(fieldAddr, fieldType,
                               ConstantOrRegister(TypedOrValueRegister(val)),
                               /* failure = */ nullptr);
 
     if (UnboxedTypeNeedsPostBarrier(fieldType))
         BaselineEmitPostWriteBarrierSlot(masm, obj, val, scratch, LiveGeneralRegisterSet(), cx_);
     return true;
 }
@@ -951,59 +951,34 @@ BaselineCacheIRCompiler::emitStoreTypedO
     // Allocate the fixed registers first. These need to be fixed for
     // callTypeUpdateIC.
     AutoScratchRegister scratch1(allocator, masm, R1.scratchReg());
     ValueOperand val = allocator.useFixedValueRegister(masm, reader.valOperandId(), R0);
 
     Register obj = allocator.useRegister(masm, objId);
     AutoScratchRegister scratch2(allocator, masm);
 
-    // We don't need a type update IC if the property is always a string.scratch
+    // We don't need a type update IC if the property is always a string.
     if (type != ReferenceTypeDescr::TYPE_STRING) {
         LiveGeneralRegisterSet saveRegs;
         saveRegs.add(obj);
         saveRegs.add(val);
         if (!callTypeUpdateIC(obj, val, scratch1, saveRegs))
             return false;
     }
 
     // Compute the address being written to.
     LoadTypedThingData(masm, layout, obj, scratch1);
     masm.addPtr(offsetAddr, scratch1);
     Address dest(scratch1, 0);
 
-    switch (type) {
-      case ReferenceTypeDescr::TYPE_ANY:
-        EmitPreBarrier(masm, dest, MIRType::Value);
-        masm.storeValue(val, dest);
-        break;
-
-      case ReferenceTypeDescr::TYPE_OBJECT: {
-        EmitPreBarrier(masm, dest, MIRType::Object);
-        Label isNull, done;
-        masm.branchTestObject(Assembler::NotEqual, val, &isNull);
-        masm.unboxObject(val, scratch2);
-        masm.storePtr(scratch2, dest);
-        masm.jump(&done);
-        masm.bind(&isNull);
-        masm.storePtr(ImmWord(0), dest);
-        masm.bind(&done);
-        break;
-      }
-
-      case ReferenceTypeDescr::TYPE_STRING:
-        EmitPreBarrier(masm, dest, MIRType::String);
-        masm.unboxString(val, scratch2);
-        masm.storePtr(scratch2, dest);
-        break;
-    }
+    emitStoreTypedObjectReferenceProp(val, type, dest, scratch2);
 
     if (type != ReferenceTypeDescr::TYPE_STRING)
         BaselineEmitPostWriteBarrierSlot(masm, obj, val, scratch1, LiveGeneralRegisterSet(), cx_);
-
     return true;
 }
 
 bool
 BaselineCacheIRCompiler::emitStoreTypedObjectScalarProperty()
 {
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     Address offsetAddr = stubAddress(reader.stubOffset());
@@ -1298,17 +1273,17 @@ BaselineCacheIRCompiler::emitStoreUnboxe
     }
 
     // Load obj->elements.
     masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), scratch);
 
     // Note that the storeUnboxedProperty call here is infallible, as the
     // IR emitter is responsible for guarding on |val|'s type.
     BaseIndex element(scratch, index, ScaleFromElemWidth(UnboxedTypeSize(elementType)));
-    EmitUnboxedPreBarrierForBaseline(masm, element, elementType);
+    EmitICUnboxedPreBarrier(masm, element, elementType);
     masm.storeUnboxedProperty(element, elementType,
                               ConstantOrRegister(TypedOrValueRegister(val)),
                               /* failure = */ nullptr);
 
     if (UnboxedTypeNeedsPostBarrier(elementType))
         BaselineEmitPostWriteBarrierSlot(masm, obj, val, scratch, LiveGeneralRegisterSet(), cx_);
     return true;
 }
@@ -1365,23 +1340,23 @@ BaselineCacheIRCompiler::emitStoreUnboxe
 
     // If length is now <= index, increment length.
     Address length(obj, UnboxedArrayObject::offsetOfLength());
     Label skipIncrementLength;
     masm.branch32(Assembler::Above, length, index, &skipIncrementLength);
     masm.add32(Imm32(1), length);
     masm.bind(&skipIncrementLength);
 
-    // Skip EmitUnboxedPreBarrierForBaseline as the memory is uninitialized.
+    // Skip EmitICUnboxedPreBarrier as the memory is uninitialized.
     masm.jump(&doStore);
 
     masm.bind(&inBounds);
 
     BaseIndex element(scratch, index, ScaleFromElemWidth(UnboxedTypeSize(elementType)));
-    EmitUnboxedPreBarrierForBaseline(masm, element, elementType);
+    EmitICUnboxedPreBarrier(masm, element, elementType);
 
     // Note that the storeUnboxedProperty call here is infallible, as the
     // IR emitter is responsible for guarding on |val|'s type.
     masm.bind(&doStore);
     masm.storeUnboxedProperty(element, elementType,
                               ConstantOrRegister(TypedOrValueRegister(val)),
                               /* failure = */ nullptr);
 
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -898,16 +898,25 @@ LoadTypedThingLength(MacroAssembler& mas
         masm.loadPtr(Address(result, ObjectGroup::offsetOfAddendum()), result);
         masm.unboxInt32(Address(result, ArrayTypeDescr::offsetOfLength()), result);
         break;
       default:
         MOZ_CRASH();
     }
 }
 
+static void
+SetUpdateStubData(ICCacheIR_Updated* stub, const PropertyTypeCheckInfo* info)
+{
+    if (info->isSet()) {
+        stub->updateStubGroup() = info->group();
+        stub->updateStubId() = info->id();
+    }
+}
+
 static bool
 DoSetElemFallback(JSContext* cx, BaselineFrame* frame, ICSetElem_Fallback* stub_, Value* stack,
                   HandleValue objv, HandleValue index, HandleValue rhs)
 {
     // This fallback stub may trigger debug mode toggling.
     DebugModeOSRVolatileStub<ICSetElem_Fallback*> stub(frame, stub_);
 
     RootedScript script(cx, frame->script());
@@ -948,20 +957,17 @@ DoSetElemFallback(JSContext* cx, Baselin
                                objv, index, rhs);
         if (gen.tryAttachStub()) {
             ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
                                                         ICStubEngine::Baseline, frame->script(), stub);
             if (newStub) {
                 JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
                 attached = true;
 
-                if (gen.needUpdateStub()) {
-                    newStub->toCacheIR_Updated()->updateStubGroup() = gen.updateStubGroup();
-                    newStub->toCacheIR_Updated()->updateStubId() = gen.updateStubId();
-                }
+                SetUpdateStubData(newStub->toCacheIR_Updated(), gen.typeCheckInfo());
 
                 if (gen.shouldNotePreliminaryObjectStub())
                     newStub->toCacheIR_Updated()->notePreliminaryObject();
                 else if (gen.shouldUnlinkPreliminaryObjectStubs())
                     StripPreliminaryObjectStubs(cx, stub);
 
                 if (gen.attachedTypedArrayOOBStub())
                     stub->noteHasTypedArrayOOB();
@@ -1014,18 +1020,17 @@ DoSetElemFallback(JSContext* cx, Baselin
         SetPropIRGenerator gen(cx, script, pc, CacheKind::SetElem, &isTemporarilyUnoptimizable,
                                objv, index, rhs);
         if (gen.tryAttachAddSlotStub(oldGroup, oldShape)) {
             ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
                                                         ICStubEngine::Baseline, frame->script(), stub);
             if (newStub) {
                 JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
                 attached = true;
-                newStub->toCacheIR_Updated()->updateStubGroup() = gen.updateStubGroup();
-                newStub->toCacheIR_Updated()->updateStubId() = gen.updateStubId();
+                SetUpdateStubData(newStub->toCacheIR_Updated(), gen.typeCheckInfo());
                 return true;
             }
         } else {
             gen.trackNotAttached();
         }
     }
 
     return true;
@@ -1079,27 +1084,34 @@ BaselineScript::noteHasDenseAdd(uint32_t
 {
     ICEntry& entry = icEntryFromPCOffset(pcOffset);
     ICFallbackStub* stub = entry.fallbackStub();
 
     if (stub->isSetElem_Fallback())
         stub->toSetElem_Fallback()->noteHasDenseAdd();
 }
 
+template <typename T>
 void
-EmitUnboxedPreBarrierForBaseline(MacroAssembler &masm, const BaseIndex& address, JSValueType type)
+EmitICUnboxedPreBarrier(MacroAssembler& masm, const T& 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));
 }
 
+template void
+EmitICUnboxedPreBarrier(MacroAssembler& masm, const Address& address, JSValueType type);
+
+template void
+EmitICUnboxedPreBarrier(MacroAssembler& masm, const BaseIndex& address, JSValueType type);
+
 template <typename T>
 void
 BaselineStoreToTypedArray(JSContext* cx, MacroAssembler& masm, Scalar::Type type,
                           const ValueOperand& value, const T& dest, Register scratch,
                           Label* failure)
 {
     Label done;
 
@@ -1505,20 +1517,17 @@ DoSetPropFallback(JSContext* cx, Baselin
                                lhs, idVal, rhs);
         if (gen.tryAttachStub()) {
             ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
                                                         ICStubEngine::Baseline, frame->script(), stub);
             if (newStub) {
                 JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
                 attached = true;
 
-                if (gen.needUpdateStub()) {
-                    newStub->toCacheIR_Updated()->updateStubGroup() = gen.updateStubGroup();
-                    newStub->toCacheIR_Updated()->updateStubId() = gen.updateStubId();
-                }
+                SetUpdateStubData(newStub->toCacheIR_Updated(), gen.typeCheckInfo());
 
                 if (gen.shouldNotePreliminaryObjectStub())
                     newStub->toCacheIR_Updated()->notePreliminaryObject();
                 else if (gen.shouldUnlinkPreliminaryObjectStubs())
                     StripPreliminaryObjectStubs(cx, stub);
             }
         }
     }
@@ -1573,18 +1582,17 @@ DoSetPropFallback(JSContext* cx, Baselin
         SetPropIRGenerator gen(cx, script, pc, CacheKind::SetProp, &isTemporarilyUnoptimizable,
                                lhs, idVal, rhs);
         if (gen.tryAttachAddSlotStub(oldGroup, oldShape)) {
             ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
                                                         ICStubEngine::Baseline, frame->script(), stub);
             if (newStub) {
                 JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
                 attached = true;
-                newStub->toCacheIR_Updated()->updateStubGroup() = gen.updateStubGroup();
-                newStub->toCacheIR_Updated()->updateStubId() = gen.updateStubId();
+                SetUpdateStubData(newStub->toCacheIR_Updated(), gen.typeCheckInfo());
             }
         } else {
             gen.trackNotAttached();
         }
     }
 
     if (!attached && !isTemporarilyUnoptimizable)
         stub->noteUnoptimizableAccess();
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -1706,18 +1706,18 @@ 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);
+template <typename T>
+void EmitICUnboxedPreBarrier(MacroAssembler &masm, const T& address, JSValueType type);
 
 // Write an arbitrary value to a typed array or typed object address at dest.
 // If the value could not be converted to the appropriate format, jump to
 // failure.
 template <typename T>
 void BaselineStoreToTypedArray(JSContext* cx, MacroAssembler& masm, Scalar::Type type,
                                const ValueOperand& value, const T& dest, Register scratch,
                                Label* failure);
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -1920,27 +1920,27 @@ IRGenerator::maybeGuardInt32Index(const 
         return true;
     }
 
     return false;
 }
 
 SetPropIRGenerator::SetPropIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
                                        CacheKind cacheKind, bool* isTemporarilyUnoptimizable,
-                                       HandleValue lhsVal, HandleValue idVal, HandleValue rhsVal)
+                                       HandleValue lhsVal, HandleValue idVal, HandleValue rhsVal,
+                                       bool needsTypeBarrier, bool maybeHasExtraIndexedProps)
   : IRGenerator(cx, script, pc, cacheKind),
     lhsVal_(lhsVal),
     idVal_(idVal),
     rhsVal_(rhsVal),
     isTemporarilyUnoptimizable_(isTemporarilyUnoptimizable),
+    typeCheckInfo_(cx, needsTypeBarrier),
     preliminaryObjectAction_(PreliminaryObjectAction::None),
     attachedTypedArrayOOBStub_(false),
-    updateStubGroup_(cx),
-    updateStubId_(cx, JSID_EMPTY),
-    needUpdateStub_(false)
+    maybeHasExtraIndexedProps_(maybeHasExtraIndexedProps)
 {}
 
 bool
 SetPropIRGenerator::tryAttachStub()
 {
     AutoAssertNoPendingException aanpe(cx_);
 
     ValOperandId objValId(writer.setInputOperandId(0));
@@ -2055,30 +2055,30 @@ SetPropIRGenerator::tryAttachNativeSetSl
     EnsureTrackPropertyTypes(cx_, obj, id);
     if (!PropertyHasBeenMarkedNonConstant(obj, id)) {
         *isTemporarilyUnoptimizable_ = true;
         return false;
     }
 
     maybeEmitIdGuard(id);
 
-    // For Baseline, we have to guard on both the shape and group, because the
-    // type update IC applies to a single group. When we port the Ion IC, we can
-    // do a bit better and avoid the group guard if we don't have to guard on
-    // the property types.
+    // If we need a property type barrier (always in Baseline, sometimes in
+    // Ion), guard on both the shape and the group. If Ion knows the property
+    // types match, we don't need the group guard.
     NativeObject* nobj = &obj->as<NativeObject>();
-    writer.guardGroup(objId, nobj->group());
+    if (typeCheckInfo_.needsTypeBarrier())
+        writer.guardGroup(objId, nobj->group());
     writer.guardShape(objId, nobj->lastProperty());
 
     if (IsPreliminaryObject(obj))
         preliminaryObjectAction_ = PreliminaryObjectAction::NotePreliminary;
     else
         preliminaryObjectAction_ = PreliminaryObjectAction::Unlink;
 
-    setUpdateStubInfo(nobj->group(), id);
+    typeCheckInfo_.set(nobj->group(), id);
     EmitStoreSlotAndReturn(writer, objId, nobj, propShape, rhsId);
 
     trackAttached("NativeSlot");
     return true;
 }
 
 bool
 SetPropIRGenerator::tryAttachUnboxedExpandoSetSlot(HandleObject obj, ObjOperandId objId,
@@ -2097,17 +2097,17 @@ SetPropIRGenerator::tryAttachUnboxedExpa
 
     maybeEmitIdGuard(id);
     writer.guardGroup(objId, obj->group());
     ObjOperandId expandoId = writer.guardAndLoadUnboxedExpando(objId);
     writer.guardShape(expandoId, expando->lastProperty());
 
     // Property types must be added to the unboxed object's group, not the
     // expando's group (it has unknown properties).
-    setUpdateStubInfo(obj->group(), id);
+    typeCheckInfo_.set(obj->group(), id);
     EmitStoreSlotAndReturn(writer, expandoId, expando, propShape, rhsId);
 
     trackAttached("UnboxedExpando");
     return true;
 }
 
 static void
 EmitGuardUnboxedPropertyType(CacheIRWriter& writer, JSValueType propType, ValOperandId valId)
@@ -2134,17 +2134,17 @@ SetPropIRGenerator::tryAttachUnboxedProp
     maybeEmitIdGuard(id);
     writer.guardGroup(objId, obj->group());
     EmitGuardUnboxedPropertyType(writer, property->type, rhsId);
     writer.storeUnboxedProperty(objId, property->type,
                                 UnboxedPlainObject::offsetOfData() + property->offset,
                                 rhsId);
     writer.returnFromIC();
 
-    setUpdateStubInfo(obj->group(), id);
+    typeCheckInfo_.set(obj->group(), id);
     preliminaryObjectAction_ = PreliminaryObjectAction::Unlink;
 
     trackAttached("Unboxed");
     return true;
 }
 
 bool
 SetPropIRGenerator::tryAttachTypedObjectProperty(HandleObject obj, ObjOperandId objId, HandleId id,
@@ -2171,17 +2171,17 @@ SetPropIRGenerator::tryAttachTypedObject
     uint32_t fieldOffset = structDescr->fieldOffset(fieldIndex);
     TypedThingLayout layout = GetTypedThingLayout(obj->getClass());
 
     maybeEmitIdGuard(id);
     writer.guardNoDetachedTypedObjects();
     writer.guardShape(objId, obj->as<TypedObject>().shape());
     writer.guardGroup(objId, obj->group());
 
-    setUpdateStubInfo(obj->group(), id);
+    typeCheckInfo_.set(obj->group(), id);
 
     // Scalar types can always be stored without a type update stub.
     if (fieldDescr->is<ScalarTypeDescr>()) {
         Scalar::Type type = fieldDescr->as<ScalarTypeDescr>().type();
         writer.storeTypedObjectScalarProperty(objId, fieldOffset, layout, type, rhsId);
         writer.returnFromIC();
 
         trackAttached("TypedObject");
@@ -2343,24 +2343,25 @@ SetPropIRGenerator::tryAttachSetDenseEle
 {
     if (!obj->isNative())
         return false;
 
     NativeObject* nobj = &obj->as<NativeObject>();
     if (!nobj->containsDenseElement(index) || nobj->getElementsHeader()->isFrozen())
         return false;
 
-    writer.guardGroup(objId, nobj->group());
+    if (typeCheckInfo_.needsTypeBarrier())
+        writer.guardGroup(objId, nobj->group());
     writer.guardShape(objId, nobj->shape());
 
     writer.storeDenseElement(objId, indexId, rhsId);
     writer.returnFromIC();
 
     // Type inference uses JSID_VOID for the element types.
-    setUpdateStubInfo(nobj->group(), JSID_VOID);
+    typeCheckInfo_.set(nobj->group(), JSID_VOID);
 
     trackAttached("SetDenseElement");
     return true;
 }
 
 static bool
 CanAttachAddElement(JSObject* obj, bool isInit)
 {
@@ -2455,28 +2456,30 @@ SetPropIRGenerator::tryAttachSetDenseEle
         return false;
 
     MOZ_ASSERT(!nobj->is<TypedArrayObject>());
 
     // Check for other indexed properties or class hooks.
     if (!CanAttachAddElement(nobj, IsPropertyInitOp(op)))
         return false;
 
-    writer.guardGroup(objId, nobj->group());
+    if (typeCheckInfo_.needsTypeBarrier())
+        writer.guardGroup(objId, nobj->group());
     writer.guardShape(objId, nobj->shape());
 
-    // Also shape guard the proto chain, unless this is an INITELEM.
-    if (IsPropertySetOp(op))
+    // Also shape guard the proto chain, unless this is an INITELEM or we know
+    // the proto chain has no indexed props.
+    if (IsPropertySetOp(op) && maybeHasExtraIndexedProps_)
         ShapeGuardProtoChain(writer, obj, objId);
 
     writer.storeDenseElementHole(objId, indexId, rhsId, isAdd);
     writer.returnFromIC();
 
     // Type inference uses JSID_VOID for the element types.
-    setUpdateStubInfo(nobj->group(), JSID_VOID);
+    typeCheckInfo_.set(nobj->group(), JSID_VOID);
 
     trackAttached(isAdd ? "AddDenseElement" : "StoreDenseElementHole");
     return true;
 }
 
 bool
 SetPropIRGenerator::tryAttachSetUnboxedArrayElement(HandleObject obj, ObjOperandId objId,
                                                     uint32_t index, Int32OperandId indexId,
@@ -2495,17 +2498,17 @@ SetPropIRGenerator::tryAttachSetUnboxedA
 
     JSValueType elementType = obj->group()->unboxedLayoutDontCheckGeneration().elementType();
     EmitGuardUnboxedPropertyType(writer, elementType, rhsId);
 
     writer.storeUnboxedArrayElement(objId, indexId, rhsId, elementType);
     writer.returnFromIC();
 
     // Type inference uses JSID_VOID for the element types.
-    setUpdateStubInfo(obj->group(), JSID_VOID);
+    typeCheckInfo_.set(obj->group(), JSID_VOID);
 
     trackAttached("SetUnboxedArrayElement");
     return true;
 }
 
 bool
 SetPropIRGenerator::tryAttachSetTypedElement(HandleObject obj, ObjOperandId objId,
                                              uint32_t index, Int32OperandId indexId,
@@ -2588,17 +2591,17 @@ SetPropIRGenerator::tryAttachSetUnboxedA
     // Also shape guard the proto chain, unless this is an INITELEM.
     if (IsPropertySetOp(op))
         ShapeGuardProtoChain(writer, aobj, objId);
 
     writer.storeUnboxedArrayElementHole(objId, indexId, rhsId, elementType);
     writer.returnFromIC();
 
     // Type inference uses JSID_VOID for the element types.
-    setUpdateStubInfo(aobj->group(), JSID_VOID);
+    typeCheckInfo_.set(aobj->group(), JSID_VOID);
 
     trackAttached("StoreUnboxedArrayElementHole");
     return true;
 }
 
 bool
 SetPropIRGenerator::tryAttachGenericProxy(HandleObject obj, ObjOperandId objId, HandleId id,
                                           ValOperandId rhsId, bool handleDOMProxies)
@@ -2878,11 +2881,11 @@ SetPropIRGenerator::tryAttachAddSlotStub
             MOZ_ASSERT(numNewSlots > numOldSlots);
             writer.allocateAndStoreDynamicSlot(holderId, offset, rhsValId, propShape,
                                                changeGroup, newGroup, numNewSlots);
             trackAttached("AllocateSlot");
         }
     }
     writer.returnFromIC();
 
-    setUpdateStubInfo(oldGroup, id);
+    typeCheckInfo_.set(oldGroup, id);
     return true;
 }
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -120,16 +120,19 @@ class TypedOperandId : public OperandId
       : OperandId(id.id()), type_(JSVAL_TYPE_STRING)
     {}
     MOZ_IMPLICIT TypedOperandId(SymbolOperandId id)
       : OperandId(id.id()), type_(JSVAL_TYPE_SYMBOL)
     {}
     MOZ_IMPLICIT TypedOperandId(Int32OperandId id)
       : OperandId(id.id()), type_(JSVAL_TYPE_INT32)
     {}
+    TypedOperandId(ValOperandId val, JSValueType type)
+      : OperandId(val.id()), type_(type)
+    {}
 
     JSValueType type() const { return type_; }
 };
 
 #define CACHE_IR_KINDS(_)   \
     _(GetProp)              \
     _(GetElem)              \
     _(GetName)              \
@@ -1048,39 +1051,60 @@ class MOZ_RAII GetNameIRGenerator : publ
 
   public:
     GetNameIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
                        HandleObject env, HandlePropertyName name);
 
     bool tryAttachStub();
 };
 
+// Information used by SetProp/SetElem stubs to check/update property types.
+class MOZ_RAII PropertyTypeCheckInfo
+{
+    RootedObjectGroup group_;
+    RootedId id_;
+    bool needsTypeBarrier_;
+
+    PropertyTypeCheckInfo(const PropertyTypeCheckInfo&) = delete;
+    void operator=(const PropertyTypeCheckInfo&) = delete;
+
+  public:
+    PropertyTypeCheckInfo(JSContext* cx, bool needsTypeBarrier)
+      : group_(cx), id_(cx), needsTypeBarrier_(needsTypeBarrier)
+    {}
+
+    bool needsTypeBarrier() const { return needsTypeBarrier_; }
+    bool isSet() const { return group_ != nullptr; }
+    ObjectGroup* group() const { MOZ_ASSERT(isSet()); return group_; }
+    jsid id() const { MOZ_ASSERT(isSet()); return id_; }
+
+    void set(ObjectGroup* group, jsid id) {
+        MOZ_ASSERT(!group_);
+        MOZ_ASSERT(group);
+        if (needsTypeBarrier_) {
+            group_ = group;
+            id_ = id;
+        }
+    }
+};
+
 // SetPropIRGenerator generates CacheIR for a SetProp IC.
 class MOZ_RAII SetPropIRGenerator : public IRGenerator
 {
     HandleValue lhsVal_;
     HandleValue idVal_;
     HandleValue rhsVal_;
     bool* isTemporarilyUnoptimizable_;
+    PropertyTypeCheckInfo typeCheckInfo_;
 
     enum class PreliminaryObjectAction { None, Unlink, NotePreliminary };
     PreliminaryObjectAction preliminaryObjectAction_;
     bool attachedTypedArrayOOBStub_;
 
-    // If Baseline needs an update stub, this contains information to create it.
-    RootedObjectGroup updateStubGroup_;
-    RootedId updateStubId_;
-    bool needUpdateStub_;
-
-    void setUpdateStubInfo(ObjectGroup* group, jsid id) {
-        MOZ_ASSERT(!needUpdateStub_);
-        needUpdateStub_ = true;
-        updateStubGroup_ = group;
-        updateStubId_ = id;
-    }
+    bool maybeHasExtraIndexedProps_;
 
     ValOperandId setElemKeyValueId() const {
         MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
         return ValOperandId(1);
     }
     ValOperandId rhsValueId() const {
         if (cacheKind_ == CacheKind::SetProp)
             return ValOperandId(1);
@@ -1126,43 +1150,37 @@ class MOZ_RAII SetPropIRGenerator : publ
     bool tryAttachProxy(HandleObject obj, ObjOperandId objId, HandleId id, ValOperandId rhsId);
     bool tryAttachProxyElement(HandleObject obj, ObjOperandId objId, ValOperandId rhsId);
 
     void trackAttached(const char* name);
 
   public:
     SetPropIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, CacheKind cacheKind,
                        bool* isTemporarilyUnoptimizable, HandleValue lhsVal, HandleValue idVal,
-                       HandleValue rhsVal);
+                       HandleValue rhsVal, bool needsTypeBarrier = true,
+                       bool maybeHasExtraIndexedProps = true);
 
     bool tryAttachStub();
     bool tryAttachAddSlotStub(HandleObjectGroup oldGroup, HandleShape oldShape);
     void trackNotAttached();
 
     bool shouldUnlinkPreliminaryObjectStubs() const {
         return preliminaryObjectAction_ == PreliminaryObjectAction::Unlink;
     }
     bool shouldNotePreliminaryObjectStub() const {
         return preliminaryObjectAction_ == PreliminaryObjectAction::NotePreliminary;
     }
 
-    bool needUpdateStub() const { return needUpdateStub_; }
+    const PropertyTypeCheckInfo* typeCheckInfo() const {
+        return &typeCheckInfo_;
+    }
 
     bool attachedTypedArrayOOBStub() const {
         return attachedTypedArrayOOBStub_;
     }
-
-    ObjectGroup* updateStubGroup() const {
-        MOZ_ASSERT(updateStubGroup_);
-        return updateStubGroup_;
-    }
-    jsid updateStubId() const {
-        MOZ_ASSERT(needUpdateStub_);
-        return updateStubId_;
-    }
 };
 
 // InIRGenerator generates CacheIR for a In IC.
 class MOZ_RAII InIRGenerator : public IRGenerator
 {
     HandleValue key_;
     HandleObject obj_;
 
--- a/js/src/jit/CacheIRCompiler.cpp
+++ b/js/src/jit/CacheIRCompiler.cpp
@@ -2,16 +2,17 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/CacheIRCompiler.h"
 
 #include "jit/IonIC.h"
+#include "jit/SharedICHelpers.h"
 
 #include "jscompartmentinlines.h"
 
 #include "jit/MacroAssembler-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
@@ -44,31 +45,42 @@ CacheRegisterAllocator::useValueRegister
       case OperandLocation::Constant: {
         ValueOperand reg = allocateValueRegister(masm);
         masm.moveValue(loc.constant(), reg);
         loc.setValueReg(reg);
         return reg;
       }
 
       case OperandLocation::PayloadReg: {
+        // Temporarily add the payload register to currentOpRegs_ so
+        // allocateValueRegister will stay away from it.
+        currentOpRegs_.add(loc.payloadReg());
         ValueOperand reg = allocateValueRegister(masm);
         masm.tagValue(loc.payloadType(), loc.payloadReg(), reg);
-        MOZ_ASSERT(!currentOpRegs_.has(loc.payloadReg()), "Payload register shouldn't be in use");
+        currentOpRegs_.take(loc.payloadReg());
         availableRegs_.add(loc.payloadReg());
         loc.setValueReg(reg);
         return reg;
       }
 
       case OperandLocation::PayloadStack: {
         ValueOperand reg = allocateValueRegister(masm);
         popPayload(masm, &loc, reg.scratchReg());
         masm.tagValue(loc.payloadType(), reg.scratchReg(), reg);
         loc.setValueReg(reg);
         return reg;
       }
+
+      case OperandLocation::DoubleReg: {
+        ValueOperand reg = allocateValueRegister(masm);
+        masm.boxDouble(loc.doubleReg(), reg);
+        loc.setValueReg(reg);
+        return reg;
+      }
+
       case OperandLocation::Uninitialized:
         break;
     }
 
     MOZ_CRASH();
 }
 
 ValueOperand
@@ -99,16 +111,19 @@ CacheRegisterAllocator::useFixedValueReg
         masm.tagValue(loc.payloadType(), loc.payloadReg(), reg);
         MOZ_ASSERT(!currentOpRegs_.has(loc.payloadReg()), "Register shouldn't be in use");
         availableRegs_.add(loc.payloadReg());
         break;
       case OperandLocation::PayloadStack:
         popPayload(masm, &loc, reg.scratchReg());
         masm.tagValue(loc.payloadType(), reg.scratchReg(), reg);
         break;
+      case OperandLocation::DoubleReg:
+        masm.boxDouble(loc.doubleReg(), reg);
+        break;
       case OperandLocation::Uninitialized:
         MOZ_CRASH();
     }
 
     loc.setValueReg(reg);
     return reg;
 }
 
@@ -174,16 +189,47 @@ CacheRegisterAllocator::useRegister(Macr
         else if (v.isSymbol())
             masm.movePtr(ImmGCPtr(v.toSymbol()), reg);
         else
             MOZ_CRASH("Unexpected Value");
         loc.setPayloadReg(reg, v.extractNonDoubleType());
         return reg;
       }
 
+      case OperandLocation::DoubleReg:
+      case OperandLocation::Uninitialized:
+        break;
+    }
+
+    MOZ_CRASH();
+}
+
+ConstantOrRegister
+CacheRegisterAllocator::useConstantOrRegister(MacroAssembler& masm, ValOperandId val)
+{
+    OperandLocation& loc = operandLocations_[val.id()];
+    switch (loc.kind()) {
+      case OperandLocation::Constant:
+        return loc.constant();
+
+      case OperandLocation::PayloadReg:
+      case OperandLocation::PayloadStack: {
+        JSValueType payloadType = loc.payloadType();
+        Register reg = useRegister(masm, TypedOperandId(val, payloadType));
+        return TypedOrValueRegister(MIRTypeFromValueType(payloadType), AnyRegister(reg));
+      }
+
+      case OperandLocation::ValueReg:
+      case OperandLocation::ValueStack:
+      case OperandLocation::BaselineFrame:
+        return TypedOrValueRegister(useValueRegister(masm, val));
+
+      case OperandLocation::DoubleReg:
+        return TypedOrValueRegister(MIRType::Double, AnyRegister(loc.doubleReg()));
+
       case OperandLocation::Uninitialized:
         break;
     }
 
     MOZ_CRASH();
 }
 
 Register
@@ -226,16 +272,17 @@ CacheRegisterAllocator::freeDeadOperandR
           case OperandLocation::ValueReg:
             availableRegs_.add(loc.valueReg());
             break;
           case OperandLocation::Uninitialized:
           case OperandLocation::PayloadStack:
           case OperandLocation::ValueStack:
           case OperandLocation::BaselineFrame:
           case OperandLocation::Constant:
+          case OperandLocation::DoubleReg:
             break;
         }
         loc.setUninitialized();
     }
 }
 
 void
 CacheRegisterAllocator::discardStack(MacroAssembler& masm)
@@ -384,37 +431,74 @@ CacheRegisterAllocator::initAvailableReg
 {
     // Registers not in availableRegs_ and not used by input operands are
     // available after being spilled.
     availableRegsAfterSpill_.set() =
         GeneralRegisterSet::Intersect(GeneralRegisterSet::Not(availableRegs_.set()),
                                       GeneralRegisterSet::Not(inputRegisterSet()));
 }
 
+void
+CacheRegisterAllocator::fixupAliasedInputs(MacroAssembler& masm)
+{
+    // If IC inputs alias each other, make sure they are stored in different
+    // locations so we don't have to deal with this complexity in the rest of
+    // the allocator.
+    //
+    // Note that this can happen in IonMonkey with something like |o.foo = o|
+    // or |o[i] = i|.
+
+    size_t numInputs = writer_.numInputOperands();
+    MOZ_ASSERT(origInputLocations_.length() == numInputs);
+
+    for (size_t i = 1; i < numInputs; i++) {
+        OperandLocation& loc1 = operandLocations_[i];
+        if (!loc1.isInRegister())
+            continue;
+
+        for (size_t j = 0; j < i; j++) {
+            OperandLocation& loc2 = operandLocations_[j];
+            if (!loc1.aliasesReg(loc2))
+                continue;
+
+            if (loc1.kind() == OperandLocation::ValueReg) {
+                MOZ_ASSERT_IF(loc2.kind() == OperandLocation::ValueReg,
+                              loc1 == loc2);
+                spillOperandToStack(masm, &loc1);
+                break;
+            }
+
+            MOZ_ASSERT(loc1.kind() == OperandLocation::PayloadReg);
+            spillOperandToStack(masm, &loc2);
+        }
+    }
+}
+
 GeneralRegisterSet
 CacheRegisterAllocator::inputRegisterSet() const
 {
     MOZ_ASSERT(origInputLocations_.length() == writer_.numInputOperands());
 
     AllocatableGeneralRegisterSet result;
     for (size_t i = 0; i < writer_.numInputOperands(); i++) {
         const OperandLocation& loc = operandLocations_[i];
         MOZ_ASSERT(loc == origInputLocations_[i]);
 
         switch (loc.kind()) {
           case OperandLocation::PayloadReg:
-            result.add(loc.payloadReg());
+            result.addUnchecked(loc.payloadReg());
             continue;
           case OperandLocation::ValueReg:
-            result.add(loc.valueReg());
+            result.addUnchecked(loc.valueReg());
             continue;
           case OperandLocation::PayloadStack:
           case OperandLocation::ValueStack:
           case OperandLocation::BaselineFrame:
           case OperandLocation::Constant:
+          case OperandLocation::DoubleReg:
             continue;
           case OperandLocation::Uninitialized:
             break;
         }
         MOZ_CRASH("Invalid kind");
     }
 
     return result.set();
@@ -435,30 +519,35 @@ CacheRegisterAllocator::knownType(ValOpe
       case OperandLocation::PayloadReg:
         return loc.payloadType();
 
       case OperandLocation::Constant:
         return loc.constant().isDouble()
                ? JSVAL_TYPE_DOUBLE
                : loc.constant().extractNonDoubleType();
 
+      case OperandLocation::DoubleReg:
+        return JSVAL_TYPE_DOUBLE;
+
       case OperandLocation::Uninitialized:
         break;
     }
 
     MOZ_CRASH("Invalid kind");
 }
 
 void
 CacheRegisterAllocator::initInputLocation(size_t i, const TypedOrValueRegister& reg)
 {
     if (reg.hasValue()) {
         initInputLocation(i, reg.valueReg());
+    } else if (reg.typedReg().isFloat()) {
+        MOZ_ASSERT(reg.type() == MIRType::Double);
+        initInputLocation(i, reg.typedReg().fpu());
     } else {
-        MOZ_ASSERT(!reg.typedReg().isFloat());
         initInputLocation(i, reg.typedReg().gpr(), ValueTypeFromMIRType(reg.type()));
     }
 }
 
 void
 CacheRegisterAllocator::initInputLocation(size_t i, const ConstantOrRegister& value)
 {
     if (value.constant())
@@ -561,16 +650,17 @@ OperandLocation::aliasesReg(const Operan
       case PayloadReg:
         return aliasesReg(other.payloadReg());
       case ValueReg:
         return aliasesReg(other.valueReg());
       case PayloadStack:
       case ValueStack:
       case BaselineFrame:
       case Constant:
+      case DoubleReg:
         return false;
       case Uninitialized:
         break;
     }
 
     MOZ_CRASH("Invalid kind");
 }
 
@@ -612,16 +702,17 @@ CacheRegisterAllocator::restoreInputStat
                 masm.tagValue(cur.payloadType(), scratch, dest.valueReg());
                 continue;
               }
               case OperandLocation::ValueStack:
                 popValue(masm, &cur, dest.valueReg());
                 continue;
               case OperandLocation::Constant:
               case OperandLocation::BaselineFrame:
+              case OperandLocation::DoubleReg:
               case OperandLocation::Uninitialized:
                 break;
             }
         } else if (dest.kind() == OperandLocation::PayloadReg) {
             // We have to restore a payload register.
             switch (cur.kind()) {
               case OperandLocation::ValueReg:
                 MOZ_ASSERT(dest.payloadType() != JSVAL_TYPE_DOUBLE);
@@ -640,21 +731,23 @@ CacheRegisterAllocator::restoreInputStat
                 MOZ_ASSERT(stackPushed_ >= sizeof(js::Value));
                 MOZ_ASSERT(cur.valueStack() <= stackPushed_);
                 MOZ_ASSERT(dest.payloadType() != JSVAL_TYPE_DOUBLE);
                 masm.unboxNonDouble(Address(masm.getStackPointer(), stackPushed_ - cur.valueStack()),
                                     dest.payloadReg());
                 continue;
               case OperandLocation::Constant:
               case OperandLocation::BaselineFrame:
+              case OperandLocation::DoubleReg:
               case OperandLocation::Uninitialized:
                 break;
             }
         } else if (dest.kind() == OperandLocation::Constant ||
-                   dest.kind() == OperandLocation::BaselineFrame)
+                   dest.kind() == OperandLocation::BaselineFrame ||
+                   dest.kind() == OperandLocation::DoubleReg)
         {
             // Nothing to do.
             continue;
         }
 
         MOZ_CRASH("Invalid kind");
     }
 
@@ -979,16 +1072,18 @@ OperandLocation::operator==(const Operan
       case PayloadStack:
         return payloadStack() == other.payloadStack() && payloadType() == other.payloadType();
       case ValueStack:
         return valueStack() == other.valueStack();
       case BaselineFrame:
         return baselineFrameSlot() == other.baselineFrameSlot();
       case Constant:
         return constant() == other.constant();
+      case DoubleReg:
+        return doubleReg() == other.doubleReg();
     }
 
     MOZ_CRASH("Invalid OperandLocation kind");
 }
 
 AutoOutputRegister::AutoOutputRegister(CacheIRCompiler& compiler)
   : output_(compiler.outputUnchecked_.ref()),
     alloc_(compiler.allocator)
@@ -1398,17 +1493,16 @@ CacheIRCompiler::emitGuardAndGetIndexFro
 
     FailurePath* failure;
     if (!addFailurePath(&failure))
         return false;
 
     AllocatableRegisterSet regs(RegisterSet::Volatile());
     LiveRegisterSet save(regs.asLiveSet());
     masm.PushRegsInMask(save);
-    regs.takeUnchecked(str);
 
     masm.setupUnalignedABICall(output);
     masm.passABIArg(str);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, GetIndexFromString));
     masm.mov(ReturnReg, output);
 
     LiveRegisterSet ignore;
     ignore.add(output);
@@ -1946,16 +2040,48 @@ CacheIRCompiler::emitLoadTypedObjectResu
             break;
 
           default:
             MOZ_CRASH("Invalid ReferenceTypeDescr");
         }
     }
 }
 
+void
+CacheIRCompiler::emitStoreTypedObjectReferenceProp(ValueOperand val, ReferenceTypeDescr::Type type,
+                                                   const Address& dest, Register scratch)
+{
+    switch (type) {
+      case ReferenceTypeDescr::TYPE_ANY:
+        EmitPreBarrier(masm, dest, MIRType::Value);
+        masm.storeValue(val, dest);
+        break;
+
+      case ReferenceTypeDescr::TYPE_OBJECT: {
+        EmitPreBarrier(masm, dest, MIRType::Object);
+        Label isNull, done;
+        masm.branchTestObject(Assembler::NotEqual, val, &isNull);
+        masm.unboxObject(val, scratch);
+        masm.storePtr(scratch, dest);
+        masm.jump(&done);
+        masm.bind(&isNull);
+        masm.storePtr(ImmWord(0), dest);
+        masm.bind(&done);
+        break;
+      }
+
+      case ReferenceTypeDescr::TYPE_STRING:
+        EmitPreBarrier(masm, dest, MIRType::String);
+        masm.unboxString(val, scratch);
+        masm.storePtr(scratch, dest);
+        break;
+    }
+}
+
+
 bool
 CacheIRCompiler::emitWrapResult()
 {
     AutoOutputRegister output(*this);
     AutoScratchRegister scratch(allocator, masm);
 
     FailurePath* failure;
     if (!addFailurePath(&failure))
--- a/js/src/jit/CacheIRCompiler.h
+++ b/js/src/jit/CacheIRCompiler.h
@@ -70,31 +70,33 @@ class BaselineFrameSlot
 // OperandLocation represents the location of an OperandId. The operand is
 // either in a register or on the stack, and is either boxed or unboxed.
 class OperandLocation
 {
   public:
     enum Kind {
         Uninitialized = 0,
         PayloadReg,
+        DoubleReg,
         ValueReg,
         PayloadStack,
         ValueStack,
         BaselineFrame,
         Constant,
     };
 
   private:
     Kind kind_;
 
     union Data {
         struct {
             Register reg;
             JSValueType type;
         } payloadReg;
+        FloatRegister doubleReg;
         ValueOperand valueReg;
         struct {
             uint32_t stackPushed;
             JSValueType type;
         } payloadStack;
         uint32_t valueStackPushed;
         BaselineFrameSlot baselineFrameSlot;
         Value constant;
@@ -115,16 +117,20 @@ class OperandLocation
     ValueOperand valueReg() const {
         MOZ_ASSERT(kind_ == ValueReg);
         return data_.valueReg;
     }
     Register payloadReg() const {
         MOZ_ASSERT(kind_ == PayloadReg);
         return data_.payloadReg.reg;
     }
+    FloatRegister doubleReg() const {
+        MOZ_ASSERT(kind_ == DoubleReg);
+        return data_.doubleReg;
+    }
     uint32_t payloadStack() const {
         MOZ_ASSERT(kind_ == PayloadStack);
         return data_.payloadStack.stackPushed;
     }
     uint32_t valueStack() const {
         MOZ_ASSERT(kind_ == ValueStack);
         return data_.valueStackPushed;
     }
@@ -143,16 +149,20 @@ class OperandLocation
         return data_.baselineFrameSlot;
     }
 
     void setPayloadReg(Register reg, JSValueType type) {
         kind_ = PayloadReg;
         data_.payloadReg.reg = reg;
         data_.payloadReg.type = type;
     }
+    void setDoubleReg(FloatRegister reg) {
+        kind_ = DoubleReg;
+        data_.doubleReg = reg;
+    }
     void setValueReg(ValueOperand reg) {
         kind_ = ValueReg;
         data_.valueReg = reg;
     }
     void setPayloadStack(uint32_t stackPushed, JSValueType type) {
         kind_ = PayloadStack;
         data_.payloadStack.stackPushed = stackPushed;
         data_.payloadStack.type = type;
@@ -289,16 +299,18 @@ class MOZ_RAII CacheRegisterAllocator
 
     MOZ_MUST_USE bool init();
 
     void initAvailableRegs(const AllocatableGeneralRegisterSet& available) {
         availableRegs_ = available;
     }
     void initAvailableRegsAfterSpill();
 
+    void fixupAliasedInputs(MacroAssembler& masm);
+
     OperandLocation operandLocation(size_t i) const {
         return operandLocations_[i];
     }
     void setOperandLocation(size_t i, const OperandLocation& loc) {
         operandLocations_[i] = loc;
     }
 
     OperandLocation origInputLocation(size_t i) const {
@@ -307,16 +319,20 @@ class MOZ_RAII CacheRegisterAllocator
     void initInputLocation(size_t i, ValueOperand reg) {
         origInputLocations_[i].setValueReg(reg);
         operandLocations_[i].setValueReg(reg);
     }
     void initInputLocation(size_t i, Register reg, JSValueType type) {
         origInputLocations_[i].setPayloadReg(reg, type);
         operandLocations_[i].setPayloadReg(reg, type);
     }
+    void initInputLocation(size_t i, FloatRegister reg) {
+        origInputLocations_[i].setDoubleReg(reg);
+        operandLocations_[i].setDoubleReg(reg);
+    }
     void initInputLocation(size_t i, const Value& v) {
         origInputLocations_[i].setConstant(v);
         operandLocations_[i].setConstant(v);
     }
     void initInputLocation(size_t i, BaselineFrameSlot slot) {
         origInputLocations_[i].setBaselineFrame(slot);
         operandLocations_[i].setBaselineFrame(slot);
     }
@@ -376,16 +392,18 @@ class MOZ_RAII CacheRegisterAllocator
     Address addressOf(MacroAssembler& masm, BaselineFrameSlot slot) const;
 
     // Returns the register for the given operand. If the operand is currently
     // not in a register, it will load it into one.
     ValueOperand useValueRegister(MacroAssembler& masm, ValOperandId val);
     ValueOperand useFixedValueRegister(MacroAssembler& masm, ValOperandId valId, ValueOperand reg);
     Register useRegister(MacroAssembler& masm, TypedOperandId typedId);
 
+    ConstantOrRegister useConstantOrRegister(MacroAssembler& masm, ValOperandId val);
+
     // Allocates an output register for the given operand.
     Register defineRegister(MacroAssembler& masm, TypedOperandId typedId);
     ValueOperand defineValueRegister(MacroAssembler& masm, ValOperandId val);
 
     // Returns |val|'s JSValueType or JSVAL_TYPE_UNKNOWN.
     JSValueType knownType(ValOperandId val) const;
 
     // Emits code to restore registers and stack to the state at the start of
@@ -542,16 +560,19 @@ class MOZ_RAII CacheIRCompiler
 
     MOZ_MUST_USE bool addFailurePath(FailurePath** failure);
     MOZ_MUST_USE bool emitFailurePath(size_t i);
 
     void emitLoadTypedObjectResultShared(const Address& fieldAddr, Register scratch,
                                          TypedThingLayout layout, uint32_t typeDescr,
                                          const AutoOutputRegister& output);
 
+    void emitStoreTypedObjectReferenceProp(ValueOperand val, ReferenceTypeDescr::Type type,
+                                           const Address& dest, Register scratch);
+
 #define DEFINE_SHARED_OP(op) MOZ_MUST_USE bool emit##op();
     CACHE_IR_SHARED_OPS(DEFINE_SHARED_OP)
 #undef DEFINE_SHARED_OP
 };
 
 // Ensures the IC's output register is available for writing.
 class MOZ_RAII AutoOutputRegister
 {
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -217,19 +217,24 @@ CodeGenerator::visitOutOfLineCache(OutOf
     masm.bind(&ool->entry());
 
     // Dispatch to ICs' accept functions.
     cache->accept(this, ool);
 }
 
 typedef bool (*IonGetPropertyICFn)(JSContext*, HandleScript, IonGetPropertyIC*, HandleValue, HandleValue,
                                    MutableHandleValue);
-const VMFunction IonGetPropertyICInfo =
+static const VMFunction IonGetPropertyICInfo =
     FunctionInfo<IonGetPropertyICFn>(IonGetPropertyIC::update, "IonGetPropertyIC::update");
 
+typedef bool (*IonSetPropertyICFn)(JSContext*, HandleScript, IonSetPropertyIC*, HandleObject,
+                                   HandleValue, HandleValue);
+static const VMFunction IonSetPropertyICInfo =
+    FunctionInfo<IonSetPropertyICFn>(IonSetPropertyIC::update, "IonSetPropertyIC::update");
+
 void
 CodeGenerator::visitOutOfLineICFallback(OutOfLineICFallback* ool)
 {
     LInstruction* lir = ool->lir();
     size_t cacheIndex = ool->cacheIndex();
     size_t cacheInfoIndex = ool->cacheInfoIndex();
 
     DataPtr<IonIC> ic(this, cacheIndex);
@@ -252,19 +257,36 @@ CodeGenerator::visitOutOfLineICFallback(
         callVM(IonGetPropertyICInfo, lir);
 
         StoreValueTo(getPropIC->output()).generate(this);
         restoreLiveIgnore(lir, StoreValueTo(getPropIC->output()).clobbered());
 
         masm.jump(ool->rejoin());
         return;
       }
+      case CacheKind::SetProp:
+      case CacheKind::SetElem: {
+        IonSetPropertyIC* setPropIC = ic->asSetPropertyIC();
+
+        saveLive(lir);
+
+        pushArg(setPropIC->rhs());
+        pushArg(setPropIC->id());
+        pushArg(setPropIC->object());
+        icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
+        pushArg(ImmGCPtr(gen->info().script()));
+
+        callVM(IonSetPropertyICInfo, lir);
+
+        restoreLive(lir);
+
+        masm.jump(ool->rejoin());
+        return;
+      }
       case CacheKind::GetName:
-      case CacheKind::SetProp:
-      case CacheKind::SetElem:
       case CacheKind::In:
         MOZ_CRASH("Baseline-specific for now");
     }
     MOZ_CRASH();
 }
 
 StringObject*
 MNewStringObject::templateObj() const
@@ -10273,26 +10295,32 @@ CodeGenerator::addGetPropertyCache(LInst
     }
     IonGetPropertyIC cache(kind, liveRegs, value, id, output, maybeTemp, monitoredResult,
                            allowDoubleResult);
     addIC(ins, allocateIC(cache));
 }
 
 void
 CodeGenerator::addSetPropertyCache(LInstruction* ins, LiveRegisterSet liveRegs, Register objReg,
-                                   Register temp, Register tempUnbox, FloatRegister tempDouble,
+                                   Register temp, FloatRegister tempDouble,
                                    FloatRegister tempF32, const ConstantOrRegister& id,
                                    const ConstantOrRegister& value,
                                    bool strict, bool needsTypeBarrier, bool guardHoles,
                                    jsbytecode* profilerLeavePc)
 {
-    SetPropertyIC cache(liveRegs, objReg, temp, tempUnbox, tempDouble, tempF32, id, value, strict,
-                        needsTypeBarrier, guardHoles);
-    cache.setProfilerLeavePC(profilerLeavePc);
-    addCache(ins, allocateCache(cache));
+    CacheKind kind = CacheKind::SetElem;
+    if (id.constant() && id.value().isString()) {
+        JSString* idString = id.value().toString();
+        uint32_t dummy;
+        if (idString->isAtom() && !idString->asAtom().isIndex(&dummy))
+            kind = CacheKind::SetProp;
+    }
+    IonSetPropertyIC cache(kind, liveRegs, objReg, temp, tempDouble, tempF32,
+                           id, value, strict, needsTypeBarrier, guardHoles);
+    addIC(ins, allocateIC(cache));
 }
 
 ConstantOrRegister
 CodeGenerator::toConstantOrRegister(LInstruction* lir, size_t n, MIRType type)
 {
     if (type == MIRType::Value)
         return TypedOrValueRegister(ToValue(lir, n));
 
@@ -10422,52 +10450,29 @@ CodeGenerator::visitCallDeleteElement(LC
 }
 
 void
 CodeGenerator::visitSetPropertyCache(LSetPropertyCache* ins)
 {
     LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
     Register objReg = ToRegister(ins->getOperand(0));
     Register temp = ToRegister(ins->temp());
-    Register tempUnbox = ToTempUnboxRegister(ins->tempToUnboxIndex());
     FloatRegister tempDouble = ToTempFloatRegisterOrInvalid(ins->tempDouble());
     FloatRegister tempF32 = ToTempFloatRegisterOrInvalid(ins->tempFloat32());
 
     ConstantOrRegister id =
         toConstantOrRegister(ins, LSetPropertyCache::Id, ins->mir()->idval()->type());
     ConstantOrRegister value =
         toConstantOrRegister(ins, LSetPropertyCache::Value, ins->mir()->value()->type());
 
-    addSetPropertyCache(ins, liveRegs, objReg, temp, tempUnbox, tempDouble, tempF32,
+    addSetPropertyCache(ins, liveRegs, objReg, temp, tempDouble, tempF32,
                         id, value, ins->mir()->strict(), ins->mir()->needsTypeBarrier(),
                         ins->mir()->guardHoles(), ins->mir()->profilerLeavePc());
 }
 
-typedef bool (*SetPropertyICFn)(JSContext*, HandleScript, size_t, HandleObject, HandleValue,
-                                HandleValue);
-const VMFunction SetPropertyIC::UpdateInfo =
-    FunctionInfo<SetPropertyICFn>(SetPropertyIC::update, "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());
-}
-
 typedef bool (*ThrowFn)(JSContext*, HandleValue);
 static const VMFunction ThrowInfoCodeGen = FunctionInfo<ThrowFn>(js::Throw, "Throw");
 
 void
 CodeGenerator::visitThrow(LThrow* lir)
 {
     pushArg(ToValue(lir, LThrow::Value));
     callVM(ThrowInfoCodeGen, lir);
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -416,17 +416,16 @@ class CodeGenerator final : public CodeG
 
     void visitGetPropertyCacheV(LGetPropertyCacheV* ins);
     void visitGetPropertyCacheT(LGetPropertyCacheT* ins);
     void visitBindNameCache(LBindNameCache* ins);
     void visitCallSetProperty(LInstruction* ins);
     void visitSetPropertyCache(LSetPropertyCache* ins);
     void visitGetNameCache(LGetNameCache* ins);
 
-    void visitSetPropertyIC(OutOfLineUpdateCache* ool, DataPtr<SetPropertyIC>& ic);
     void visitBindNameIC(OutOfLineUpdateCache* ool, DataPtr<BindNameIC>& ic);
     void visitNameIC(OutOfLineUpdateCache* ool, DataPtr<NameIC>& ic);
 
     void visitAssertRangeI(LAssertRangeI* ins);
     void visitAssertRangeD(LAssertRangeD* ins);
     void visitAssertRangeF(LAssertRangeF* ins);
     void visitAssertRangeV(LAssertRangeV* ins);
 
@@ -456,17 +455,17 @@ class CodeGenerator final : public CodeG
     }
 
   private:
     void addGetPropertyCache(LInstruction* ins, LiveRegisterSet liveRegs,
                              TypedOrValueRegister value, const ConstantOrRegister& id,
                              TypedOrValueRegister output, Register maybeTemp, bool monitoredResult,
                              bool allowDoubleResult, jsbytecode* profilerLeavePc);
     void addSetPropertyCache(LInstruction* ins, LiveRegisterSet liveRegs, Register objReg,
-                             Register temp, Register tempUnbox, FloatRegister tempDouble,
+                             Register temp, FloatRegister tempDouble,
                              FloatRegister tempF32, const ConstantOrRegister& id,
                              const ConstantOrRegister& value,
                              bool strict, bool needsTypeBarrier, bool guardHoles,
                              jsbytecode* profilerLeavePc);
 
     MOZ_MUST_USE bool generateBranchV(const ValueOperand& value, Label* ifTrue, Label* ifFalse,
                                       FloatRegister fr);
 
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -1270,16 +1270,19 @@ JS::DeletePolicy<js::jit::IonScript>::op
 {
     IonScript::Destroy(rt_->defaultFreeOp(), const_cast<IonScript*>(script));
 }
 
 void
 IonScript::toggleBarriers(bool enabled, ReprotectCode reprotect)
 {
     method()->togglePreBarriers(enabled, reprotect);
+
+    for (size_t i = 0; i < numICs(); i++)
+        getICFromIndex(i).togglePreBarriers(enabled, reprotect);
 }
 
 void
 IonScript::purgeOptimizedStubs(Zone* zone)
 {
     for (size_t i = 0; i < numSharedStubs(); i++) {
         IonICEntry& entry = sharedStubList()[i];
         if (!entry.hasStub())
--- a/js/src/jit/IonCacheIRCompiler.cpp
+++ b/js/src/jit/IonCacheIRCompiler.cpp
@@ -12,38 +12,40 @@
 
 #include "jit/Linker.h"
 #include "jit/SharedICHelpers.h"
 #include "proxy/Proxy.h"
 
 #include "jscompartmentinlines.h"
 
 #include "jit/MacroAssembler-inl.h"
+#include "vm/TypeInference-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::DebugOnly;
 
 namespace js {
 namespace jit {
 
 // IonCacheIRCompiler compiles CacheIR to IonIC native code.
 class MOZ_RAII IonCacheIRCompiler : public CacheIRCompiler
 {
   public:
     friend class AutoSaveLiveRegisters;
 
     IonCacheIRCompiler(JSContext* cx, const CacheIRWriter& writer, IonIC* ic, IonScript* ionScript,
-                       IonICStub* stub)
+                       IonICStub* stub, const PropertyTypeCheckInfo* typeCheckInfo)
       : CacheIRCompiler(cx, writer, Mode::Ion),
         writer_(writer),
         ic_(ic),
         ionScript_(ionScript),
         stub_(stub),
+        typeCheckInfo_(typeCheckInfo),
         nextStubField_(0),
 #ifdef DEBUG
         calledPrepareVMCall_(false),
 #endif
         savedLiveRegs_(false)
     {
         MOZ_ASSERT(ic_);
         MOZ_ASSERT(ionScript_);
@@ -55,16 +57,20 @@ class MOZ_RAII IonCacheIRCompiler : publ
   private:
     const CacheIRWriter& writer_;
     IonIC* ic_;
     IonScript* ionScript_;
 
     // The stub we're generating code for.
     IonICStub* stub_;
 
+    // Information necessary to generate property type checks. Non-null iff
+    // this is a SetProp/SetElem stub.
+    const PropertyTypeCheckInfo* typeCheckInfo_;
+
     CodeOffsetJump rejoinOffset_;
     Vector<CodeOffset, 4, SystemAllocPolicy> nextCodeOffsets_;
     Maybe<LiveRegisterSet> liveRegs_;
     Maybe<CodeOffset> stubJitCodeOffset_;
     uint32_t nextStubField_;
 
 #ifdef DEBUG
     bool calledPrepareVMCall_;
@@ -120,16 +126,18 @@ class MOZ_RAII IonCacheIRCompiler : publ
         uint64_t* ptr = reinterpret_cast<uint64_t*>(stub_->stubDataStart() + offset);
         MOZ_ASSERT(*ptr == generation);
         return ptr;
     }
 
     void prepareVMCall(MacroAssembler& masm);
     MOZ_MUST_USE bool callVM(MacroAssembler& masm, const VMFunction& fun);
 
+    MOZ_MUST_USE bool emitAddAndStoreSlotShared(CacheOp op);
+
     void pushStubCodePointer() {
         stubJitCodeOffset_.emplace(masm.PushWithPatch(ImmPtr((void*)-1)));
     }
 
 #define DEFINE_OP(op) MOZ_MUST_USE bool emit##op();
     CACHE_IR_OPS(DEFINE_OP)
 #undef DEFINE_OP
 };
@@ -169,18 +177,16 @@ class MOZ_RAII AutoSaveLiveRegisters
     bool IonCacheIRCompiler::emit##op() { return CacheIRCompiler::emit##op(); }
     CACHE_IR_SHARED_OPS(DEFINE_SHARED_OP)
 #undef DEFINE_SHARED_OP
 
 void
 CacheRegisterAllocator::saveIonLiveRegisters(MacroAssembler& masm, LiveRegisterSet liveRegs,
                                              Register scratch, IonScript* ionScript)
 {
-    MOZ_ASSERT(!liveRegs.has(scratch));
-
     // We have to push all registers in liveRegs on the stack. It's possible we
     // stored other values in our live registers and stored operands on the
     // stack (where our live registers should go), so this requires some careful
     // work. Try to keep it simple by taking one small step at a time.
 
     // Step 1. Discard any dead operands so we can reuse their registers.
     freeDeadOperandRegisters();
 
@@ -285,16 +291,20 @@ CacheRegisterAllocator::saveIonLiveRegis
 
     MOZ_ASSERT(masm.framePushed() == ionScript->frameSize() + sizeOfLiveRegsInBytes);
 
     // Step 7. All live registers and non-input operands are stored on the stack
     // now, so at this point all registers except for the input registers are
     // available.
     availableRegs_.set() = GeneralRegisterSet::Not(inputRegisterSet());
     availableRegsAfterSpill_.set() = GeneralRegisterSet();
+
+    // Step 8. We restored our input state, so we have to fix up aliased input
+    // registers again.
+    fixupAliasedInputs(masm);
 }
 
 void
 CacheRegisterAllocator::restoreIonLiveRegisters(MacroAssembler& masm, LiveRegisterSet liveRegs)
 {
     masm.PopRegsInMask(liveRegs);
 
     availableRegs_.set() = GeneralRegisterSet();
@@ -345,17 +355,19 @@ IonCacheIRCompiler::init()
 {
     if (!allocator.init())
         return false;
 
     size_t numInputs = writer_.numInputOperands();
 
     AllocatableGeneralRegisterSet available;
 
-    if (ic_->kind() == CacheKind::GetProp || ic_->kind() == CacheKind::GetElem) {
+    switch (ic_->kind()) {
+      case CacheKind::GetProp:
+      case CacheKind::GetElem: {
         IonGetPropertyIC* ic = ic_->asGetPropertyIC();
         TypedOrValueRegister output = ic->output();
 
         if (output.hasValue())
             available.add(output.valueReg());
         else if (!output.typedReg().isFloat())
             available.add(output.typedReg().gpr());
 
@@ -367,32 +379,57 @@ IonCacheIRCompiler::init()
 
         allowDoubleResult_.emplace(ic->allowDoubleResult());
 
         MOZ_ASSERT(numInputs == 1 || numInputs == 2);
 
         allocator.initInputLocation(0, ic->value());
         if (numInputs > 1)
             allocator.initInputLocation(1, ic->id());
-    } else {
+        break;
+      }
+      case CacheKind::SetProp:
+      case CacheKind::SetElem: {
+        IonSetPropertyIC* ic = ic_->asSetPropertyIC();
+
+        available.add(ic->temp1());
+
+        liveRegs_.emplace(ic->liveRegs());
+
+        allocator.initInputLocation(0, ic->object(), JSVAL_TYPE_OBJECT);
+
+        if (ic->kind() == CacheKind::SetProp) {
+            MOZ_ASSERT(numInputs == 2);
+            allocator.initInputLocation(1, ic->rhs());
+        } else {
+            MOZ_ASSERT(numInputs == 3);
+            allocator.initInputLocation(1, ic->id());
+            allocator.initInputLocation(2, ic->rhs());
+        }
+        break;
+      }
+      case CacheKind::GetName:
+      case CacheKind::In:
         MOZ_CRASH("Invalid cache");
     }
 
     allocator.initAvailableRegs(available);
     allocator.initAvailableRegsAfterSpill();
     return true;
 }
 
 JitCode*
 IonCacheIRCompiler::compile()
 {
     masm.setFramePushed(ionScript_->frameSize());
     if (cx_->runtime()->geckoProfiler().enabled())
         masm.enableProfilingInstrumentation();
 
+    allocator.fixupAliasedInputs(masm);
+
     do {
         switch (reader.readOp()) {
 #define DEFINE_OP(op)                   \
           case CacheOp::op:             \
             if (!emit##op())            \
                 return nullptr;         \
             break;
     CACHE_IR_OPS(DEFINE_OP)
@@ -856,122 +893,718 @@ IonCacheIRCompiler::emitLoadEnvironmentF
 }
 
 bool
 IonCacheIRCompiler::emitLoadEnvironmentDynamicSlotResult()
 {
     MOZ_CRASH("Baseline-specific op");
 }
 
+static bool
+GroupHasPropertyTypes(ObjectGroup* group, jsid* id, Value* v)
+{
+    if (group->unknownProperties())
+        return true;
+    HeapTypeSet* propTypes = group->maybeGetProperty(*id);
+    if (!propTypes)
+        return true;
+    if (!propTypes->nonConstantProperty())
+        return false;
+    return propTypes->hasType(TypeSet::GetValueType(*v));
+}
+
+static void
+EmitCheckPropertyTypes(MacroAssembler& masm, const PropertyTypeCheckInfo* typeCheckInfo,
+                       Register obj, const ConstantOrRegister& val,
+                       const LiveRegisterSet& liveRegs, Label* failures)
+{
+    // Emit code to check |val| is part of the property's HeapTypeSet.
+
+    if (!typeCheckInfo->isSet())
+        return;
+
+    ObjectGroup* group = typeCheckInfo->group();
+    if (group->unknownProperties())
+        return;
+
+    jsid id = typeCheckInfo->id();
+    HeapTypeSet* propTypes = group->maybeGetProperty(id);
+    if (propTypes && propTypes->unknown())
+        return;
+
+    // Use the object register as scratch, as we don't need it here.
+    masm.Push(obj);
+    Register scratch1 = obj;
+
+    bool checkTypeSet = true;
+    Label failedFastPath;
+
+    if (propTypes && !propTypes->nonConstantProperty())
+        masm.jump(&failedFastPath);
+
+    if (val.constant()) {
+        // If the input is a constant, then don't bother if the barrier will always fail.
+        if (!propTypes || !propTypes->hasType(TypeSet::GetValueType(val.value())))
+            masm.jump(&failedFastPath);
+        checkTypeSet = false;
+    } else {
+        // We can do the same trick as above for primitive types of specialized
+        // registers.
+        TypedOrValueRegister reg = val.reg();
+        if (reg.hasTyped() && reg.type() != MIRType::Object) {
+            JSValueType valType = ValueTypeFromMIRType(reg.type());
+            if (!propTypes || !propTypes->hasType(TypeSet::PrimitiveType(valType)))
+                masm.jump(&failedFastPath);
+            checkTypeSet = false;
+        }
+    }
+
+    Label done;
+    if (checkTypeSet) {
+        TypedOrValueRegister valReg = val.reg();
+        if (propTypes) {
+            // guardTypeSet can read from type sets without triggering read barriers.
+            TypeSet::readBarrier(propTypes);
+            masm.guardTypeSet(valReg, propTypes, BarrierKind::TypeSet, scratch1, &failedFastPath);
+            masm.jump(&done);
+        } else {
+            masm.jump(&failedFastPath);
+        }
+    }
+
+    if (failedFastPath.used()) {
+        // The inline type check failed. Do a callWithABI to check the current
+        // TypeSet in case the type was added after we generated this stub.
+        masm.bind(&failedFastPath);
+
+        AllocatableRegisterSet regs(GeneralRegisterSet::Volatile(), liveRegs.fpus());
+        LiveRegisterSet save(regs.asLiveSet());
+        masm.PushRegsInMask(save);
+
+        regs.takeUnchecked(scratch1);
+
+        // Push |val| first to make sure everything is fine if |val| aliases
+        // scratch2.
+        Register scratch2 = regs.takeAnyGeneral();
+        masm.Push(val);
+        masm.moveStackPtrTo(scratch2);
+
+        Register scratch3 = regs.takeAnyGeneral();
+        masm.Push(id, scratch3);
+        masm.moveStackPtrTo(scratch3);
+
+        masm.setupUnalignedABICall(scratch1);
+        masm.movePtr(ImmGCPtr(group), scratch1);
+        masm.passABIArg(scratch1);
+        masm.passABIArg(scratch3);
+        masm.passABIArg(scratch2);
+        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, GroupHasPropertyTypes));
+        masm.mov(ReturnReg, scratch1);
+
+        masm.adjustStack(sizeof(Value) + sizeof(jsid));
+
+        LiveRegisterSet ignore;
+        ignore.add(scratch1);
+        masm.PopRegsInMaskIgnore(save, ignore);
+
+        masm.branchIfTrueBool(scratch1, &done);
+        masm.pop(obj);
+        masm.jump(failures);
+    }
+
+    masm.bind(&done);
+    masm.Pop(obj);
+}
+
 bool
 IonCacheIRCompiler::emitStoreFixedSlot()
 {
-    MOZ_CRASH("Baseline-specific op");
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    int32_t offset = int32StubField(reader.stubOffset());
+    ConstantOrRegister val = allocator.useConstantOrRegister(masm, reader.valOperandId());
+
+    if (typeCheckInfo_->isSet()) {
+        FailurePath* failure;
+        if (!addFailurePath(&failure))
+            return false;
+
+        EmitCheckPropertyTypes(masm, typeCheckInfo_, obj, val, *liveRegs_, failure->label());
+    }
+
+    Address slot(obj, offset);
+    EmitPreBarrier(masm, slot, MIRType::Value);
+    masm.storeConstantOrRegister(val, slot);
+    return true;
 }
 
 bool
 IonCacheIRCompiler::emitStoreDynamicSlot()
 {
-    MOZ_CRASH("Baseline-specific op");
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    int32_t offset = int32StubField(reader.stubOffset());
+    ConstantOrRegister val = allocator.useConstantOrRegister(masm, reader.valOperandId());
+    AutoScratchRegister scratch(allocator, masm);
+
+    if (typeCheckInfo_->isSet()) {
+        FailurePath* failure;
+        if (!addFailurePath(&failure))
+            return false;
+
+        EmitCheckPropertyTypes(masm, typeCheckInfo_, obj, val, *liveRegs_, failure->label());
+    }
+
+    masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), scratch);
+    Address slot(scratch, offset);
+    EmitPreBarrier(masm, slot, MIRType::Value);
+    masm.storeConstantOrRegister(val, slot);
+    return true;
+}
+
+bool
+IonCacheIRCompiler::emitAddAndStoreSlotShared(CacheOp op)
+{
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    int32_t offset = int32StubField(reader.stubOffset());
+    ConstantOrRegister val = allocator.useConstantOrRegister(masm, reader.valOperandId());
+
+    AutoScratchRegister scratch1(allocator, masm);
+
+    Maybe<AutoScratchRegister> scratch2;
+    if (op == CacheOp::AllocateAndStoreDynamicSlot)
+        scratch2.emplace(allocator, masm);
+
+    bool changeGroup = reader.readBool();
+    ObjectGroup* newGroup = groupStubField(reader.stubOffset());
+    Shape* newShape = shapeStubField(reader.stubOffset());
+
+    FailurePath* failure;
+    if (!addFailurePath(&failure))
+        return false;
+
+    EmitCheckPropertyTypes(masm, typeCheckInfo_, obj, val, *liveRegs_, failure->label());
+
+    if (op == CacheOp::AllocateAndStoreDynamicSlot) {
+        // We have to (re)allocate dynamic slots. Do this first, as it's the
+        // only fallible operation here. This simplifies the callTypeUpdateIC
+        // call below: it does not have to worry about saving registers used by
+        // failure paths.
+        int32_t numNewSlots = int32StubField(reader.stubOffset());
+        MOZ_ASSERT(numNewSlots > 0);
+
+        AllocatableRegisterSet regs(RegisterSet::Volatile());
+        LiveRegisterSet save(regs.asLiveSet());
+
+        masm.PushRegsInMask(save);
+
+        masm.setupUnalignedABICall(scratch1);
+        masm.loadJSContext(scratch1);
+        masm.passABIArg(scratch1);
+        masm.passABIArg(obj);
+        masm.move32(Imm32(numNewSlots), scratch2.ref());
+        masm.passABIArg(scratch2.ref());
+        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, NativeObject::growSlotsDontReportOOM));
+        masm.mov(ReturnReg, scratch1);
+
+        LiveRegisterSet ignore;
+        ignore.add(scratch1);
+        masm.PopRegsInMaskIgnore(save, ignore);
+
+        masm.branchIfFalseBool(scratch1, failure->label());
+    }
+
+    if (changeGroup) {
+        // Changing object's group from a partially to fully initialized group,
+        // per the acquired properties analysis. Only change the group if the
+        // old group still has a newScript. This only applies to PlainObjects.
+        Label noGroupChange;
+        masm.loadPtr(Address(obj, JSObject::offsetOfGroup()), scratch1);
+        masm.branchPtr(Assembler::Equal,
+                       Address(scratch1, ObjectGroup::offsetOfAddendum()),
+                       ImmWord(0),
+                       &noGroupChange);
+
+        Address groupAddr(obj, JSObject::offsetOfGroup());
+        EmitPreBarrier(masm, groupAddr, MIRType::ObjectGroup);
+        masm.storePtr(ImmGCPtr(newGroup), groupAddr);
+
+        masm.bind(&noGroupChange);
+    }
+
+    // Update the object's shape.
+    Address shapeAddr(obj, ShapedObject::offsetOfShape());
+    EmitPreBarrier(masm, shapeAddr, MIRType::Shape);
+    masm.storePtr(ImmGCPtr(newShape), shapeAddr);
+
+    // Perform the store. No pre-barrier required since this is a new
+    // initialization.
+    if (op == CacheOp::AddAndStoreFixedSlot) {
+        Address slot(obj, offset);
+        masm.storeConstantOrRegister(val, slot);
+    } else {
+        MOZ_ASSERT(op == CacheOp::AddAndStoreDynamicSlot ||
+                   op == CacheOp::AllocateAndStoreDynamicSlot);
+        masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), scratch1);
+        Address slot(scratch1, offset);
+        masm.storeConstantOrRegister(val, slot);
+    }
+
+    return true;
 }
 
 bool
 IonCacheIRCompiler::emitAddAndStoreFixedSlot()
 {
-    MOZ_CRASH("Baseline-specific op");
+    return emitAddAndStoreSlotShared(CacheOp::AddAndStoreFixedSlot);
 }
 
 bool
 IonCacheIRCompiler::emitAddAndStoreDynamicSlot()
 {
-    MOZ_CRASH("Baseline-specific op");
+    return emitAddAndStoreSlotShared(CacheOp::AddAndStoreDynamicSlot);
 }
 
 bool
 IonCacheIRCompiler::emitAllocateAndStoreDynamicSlot()
 {
-    MOZ_CRASH("Baseline-specific op");
+    return emitAddAndStoreSlotShared(CacheOp::AllocateAndStoreDynamicSlot);
 }
 
 bool
 IonCacheIRCompiler::emitStoreUnboxedProperty()
 {
-    MOZ_CRASH("Baseline-specific op");
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    JSValueType fieldType = reader.valueType();
+    int32_t offset = int32StubField(reader.stubOffset());
+    ConstantOrRegister val = allocator.useConstantOrRegister(masm, reader.valOperandId());
+
+    if (fieldType == JSVAL_TYPE_OBJECT && typeCheckInfo_->isSet()) {
+        FailurePath* failure;
+        if (!addFailurePath(&failure))
+            return false;
+        EmitCheckPropertyTypes(masm, typeCheckInfo_, obj, val, *liveRegs_, failure->label());
+    }
+
+    // Note that the storeUnboxedProperty call here is infallible, as the
+    // IR emitter is responsible for guarding on |val|'s type.
+    Address fieldAddr(obj, offset);
+    EmitICUnboxedPreBarrier(masm, fieldAddr, fieldType);
+    masm.storeUnboxedProperty(fieldAddr, fieldType, val, /* failure = */ nullptr);
+    return true;
 }
 
 bool
 IonCacheIRCompiler::emitStoreTypedObjectReferenceProperty()
 {
-    MOZ_CRASH("Baseline-specific op");
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    int32_t offset = int32StubField(reader.stubOffset());
+    TypedThingLayout layout = reader.typedThingLayout();
+    ReferenceTypeDescr::Type type = reader.referenceTypeDescrType();
+
+    ValueOperand val = allocator.useValueRegister(masm, reader.valOperandId());
+
+    AutoScratchRegister scratch1(allocator, masm);
+    AutoScratchRegister scratch2(allocator, masm);
+
+    // We don't need to check property types if the property is always a
+    // string.
+    if (type != ReferenceTypeDescr::TYPE_STRING) {
+        FailurePath* failure;
+        if (!addFailurePath(&failure))
+            return false;
+        EmitCheckPropertyTypes(masm, typeCheckInfo_, obj, TypedOrValueRegister(val),
+                               *liveRegs_, failure->label());
+    }
+
+    // Compute the address being written to.
+    LoadTypedThingData(masm, layout, obj, scratch1);
+    Address dest(scratch1, offset);
+
+    emitStoreTypedObjectReferenceProp(val, type, dest, scratch2);
+    return true;
 }
 
 bool
 IonCacheIRCompiler::emitStoreTypedObjectScalarProperty()
 {
-    MOZ_CRASH("Baseline-specific op");
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    int32_t offset = int32StubField(reader.stubOffset());
+    TypedThingLayout layout = reader.typedThingLayout();
+    Scalar::Type type = reader.scalarType();
+    ValueOperand val = allocator.useValueRegister(masm, reader.valOperandId());
+    AutoScratchRegister scratch1(allocator, masm);
+    AutoScratchRegister scratch2(allocator, masm);
+
+    FailurePath* failure;
+    if (!addFailurePath(&failure))
+        return false;
+
+    // Compute the address being written to.
+    LoadTypedThingData(masm, layout, obj, scratch1);
+    Address dest(scratch1, offset);
+
+    BaselineStoreToTypedArray(cx_, masm, type, val, dest, scratch2, failure->label());
+    return true;
 }
 
 bool
 IonCacheIRCompiler::emitStoreDenseElement()
 {
-    MOZ_CRASH("Baseline-specific op");
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    Register index = allocator.useRegister(masm, reader.int32OperandId());
+    ConstantOrRegister val = allocator.useConstantOrRegister(masm, reader.valOperandId());
+
+    AutoScratchRegister scratch(allocator, masm);
+
+    FailurePath* failure;
+    if (!addFailurePath(&failure))
+        return false;
+
+    EmitCheckPropertyTypes(masm, typeCheckInfo_, obj, val, *liveRegs_, failure->label());
+
+    // Load obj->elements in scratch.
+    masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch);
+
+    // Bounds check.
+    Address initLength(scratch, ObjectElements::offsetOfInitializedLength());
+    masm.branch32(Assembler::BelowOrEqual, initLength, index, failure->label());
+
+    // Hole check.
+    BaseObjectElementIndex element(scratch, index);
+    masm.branchTestMagic(Assembler::Equal, element, failure->label());
+
+    EmitPreBarrier(masm, element, MIRType::Value);
+    EmitIonStoreDenseElement(masm, val, scratch, element);
+    return true;
 }
 
 bool
 IonCacheIRCompiler::emitStoreDenseElementHole()
 {
-    MOZ_CRASH("Baseline-specific op");
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    Register index = allocator.useRegister(masm, reader.int32OperandId());
+    ConstantOrRegister val = allocator.useConstantOrRegister(masm, reader.valOperandId());
+
+    // handleAdd boolean is only relevant for Baseline. Ion ICs can always
+    // handle adds as we don't have to set any flags on the fallback stub to
+    // track this.
+    reader.readBool();
+
+    AutoScratchRegister scratch(allocator, masm);
+
+    FailurePath* failure;
+    if (!addFailurePath(&failure))
+        return false;
+
+    EmitCheckPropertyTypes(masm, typeCheckInfo_, obj, val, *liveRegs_, failure->label());
+
+    // Load obj->elements in scratch.
+    masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch);
+
+    Address initLength(scratch, ObjectElements::offsetOfInitializedLength());
+    BaseObjectElementIndex element(scratch, index);
+
+    Label inBounds, doStore;
+    masm.branch32(Assembler::Above, initLength, index, &inBounds);
+    masm.branch32(Assembler::NotEqual, initLength, index, failure->label());
+
+    // Check the capacity.
+    Address capacity(scratch, ObjectElements::offsetOfCapacity());
+    masm.branch32(Assembler::BelowOrEqual, capacity, index, failure->label());
+
+    // Increment initLength.
+    masm.add32(Imm32(1), initLength);
+
+    // If length is now <= index, increment length too.
+    Label skipIncrementLength;
+    Address length(scratch, ObjectElements::offsetOfLength());
+    masm.branch32(Assembler::Above, length, index, &skipIncrementLength);
+    masm.add32(Imm32(1), length);
+    masm.bind(&skipIncrementLength);
+
+    // Skip EmitPreBarrier as the memory is uninitialized.
+    masm.jump(&doStore);
+
+    masm.bind(&inBounds);
+
+    EmitPreBarrier(masm, element, MIRType::Value);
+
+    masm.bind(&doStore);
+    EmitIonStoreDenseElement(masm, val, scratch, element);
+    return true;
 }
 
 bool
 IonCacheIRCompiler::emitStoreTypedElement()
 {
-    MOZ_CRASH("Baseline-specific op");
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    Register index = allocator.useRegister(masm, reader.int32OperandId());
+    ConstantOrRegister val = allocator.useConstantOrRegister(masm, reader.valOperandId());
+
+    TypedThingLayout layout = reader.typedThingLayout();
+    Scalar::Type arrayType = reader.scalarType();
+    bool handleOOB = reader.readBool();
+
+    AutoScratchRegister scratch1(allocator, masm);
+
+    Maybe<AutoScratchRegister> scratch2;
+    if (arrayType != Scalar::Float32 && arrayType != Scalar::Float64)
+        scratch2.emplace(allocator, masm);
+
+    FailurePath* failure;
+    if (!addFailurePath(&failure))
+        return false;
+
+    // Bounds check.
+    Label done;
+    LoadTypedThingLength(masm, layout, obj, scratch1);
+    masm.branch32(Assembler::BelowOrEqual, scratch1, index, handleOOB ? &done : failure->label());
+
+    // Load the elements vector.
+    LoadTypedThingData(masm, layout, obj, scratch1);
+
+    BaseIndex dest(scratch1, index, ScaleFromElemWidth(Scalar::byteSize(arrayType)));
+
+    FloatRegister maybeTempDouble = ic_->asSetPropertyIC()->maybeTempDouble();
+    FloatRegister maybeTempFloat32 = ic_->asSetPropertyIC()->maybeTempFloat32();
+    MOZ_ASSERT(maybeTempDouble != InvalidFloatReg);
+    MOZ_ASSERT_IF(jit::hasUnaliasedDouble(), maybeTempFloat32 != InvalidFloatReg);
+
+    if (arrayType == Scalar::Float32) {
+        FloatRegister tempFloat = hasUnaliasedDouble() ? maybeTempFloat32 : maybeTempDouble;
+        if (!masm.convertConstantOrRegisterToFloat(cx_, val, tempFloat, failure->label()))
+            return false;
+        masm.storeToTypedFloatArray(arrayType, tempFloat, dest);
+    } else if (arrayType == Scalar::Float64) {
+        if (!masm.convertConstantOrRegisterToDouble(cx_, val, maybeTempDouble, failure->label()))
+            return false;
+        masm.storeToTypedFloatArray(arrayType, maybeTempDouble, dest);
+    } else {
+        Register valueToStore = scratch2.ref();
+        if (arrayType == Scalar::Uint8Clamped) {
+            if (!masm.clampConstantOrRegisterToUint8(cx_, val, maybeTempDouble, valueToStore,
+                                                     failure->label()))
+            {
+                return false;
+            }
+        } else {
+            if (!masm.truncateConstantOrRegisterToInt32(cx_, val, maybeTempDouble, valueToStore,
+                                                        failure->label()))
+            {
+                return false;
+            }
+        }
+        masm.storeToTypedIntArray(arrayType, valueToStore, dest);
+    }
+
+    masm.bind(&done);
+    return true;
 }
 
 bool
 IonCacheIRCompiler::emitStoreUnboxedArrayElement()
 {
+    // --unboxed-arrays is currently untested and broken.
     MOZ_CRASH("Baseline-specific op");
 }
 
 bool
 IonCacheIRCompiler::emitStoreUnboxedArrayElementHole()
 {
+    // --unboxed-arrays is currently untested and broken.
     MOZ_CRASH("Baseline-specific op");
 }
 
 bool
 IonCacheIRCompiler::emitCallNativeSetter()
 {
-    MOZ_CRASH("Baseline-specific op");
+    AutoSaveLiveRegisters save(*this);
+
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    JSFunction* target = &objectStubField(reader.stubOffset())->as<JSFunction>();
+    MOZ_ASSERT(target->isNative());
+    ConstantOrRegister val = allocator.useConstantOrRegister(masm, reader.valOperandId());
+
+    AutoScratchRegister argJSContext(allocator, masm);
+    AutoScratchRegister argVp(allocator, masm);
+    AutoScratchRegister argUintN(allocator, masm);
+    AutoScratchRegister scratch(allocator, masm);
+
+    allocator.discardStack(masm);
+
+    // Set up the call:
+    //  bool (*)(JSContext*, unsigned, Value* vp)
+    // vp[0] is callee/outparam
+    // vp[1] is |this|
+    // vp[2] is the value
+
+    // Build vp and move the base into argVpReg.
+    masm.Push(val);
+    masm.Push(TypedOrValueRegister(MIRType::Object, AnyRegister(obj)));
+    masm.Push(ObjectValue(*target));
+    masm.moveStackPtrTo(argVp.get());
+
+    // Preload other regs.
+    masm.loadJSContext(argJSContext);
+    masm.move32(Imm32(1), argUintN);
+
+    // Push marking data for later use.
+    masm.Push(argUintN);
+    pushStubCodePointer();
+
+    if (!masm.icBuildOOLFakeExitFrame(GetReturnAddressToIonCode(cx_), save))
+        return false;
+    masm.enterFakeExitFrame(scratch, IonOOLNativeExitFrameLayoutToken);
+
+    // Make the call.
+    masm.setupUnalignedABICall(scratch);
+    masm.passABIArg(argJSContext);
+    masm.passABIArg(argUintN);
+    masm.passABIArg(argVp);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, target->native()));
+
+    // Test for failure.
+    masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
+
+    masm.adjustStack(IonOOLNativeExitFrameLayout::Size(1));
+    return true;
 }
 
 bool
 IonCacheIRCompiler::emitCallScriptedSetter()
 {
-    MOZ_CRASH("Baseline-specific op");
+    AutoSaveLiveRegisters save(*this);
+
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    JSFunction* target = &objectStubField(reader.stubOffset())->as<JSFunction>();
+    ConstantOrRegister val = allocator.useConstantOrRegister(masm, reader.valOperandId());
+
+    AutoScratchRegister scratch(allocator, masm);
+
+    allocator.discardStack(masm);
+
+    uint32_t framePushedBefore = masm.framePushed();
+
+    // Construct IonICCallFrameLayout.
+    uint32_t descriptor = MakeFrameDescriptor(masm.framePushed(), JitFrame_IonJS,
+                                              IonICCallFrameLayout::Size());
+    pushStubCodePointer();
+    masm.Push(Imm32(descriptor));
+    masm.Push(ImmPtr(GetReturnAddressToIonCode(cx_)));
+
+    // The JitFrameLayout pushed below will be aligned to JitStackAlignment,
+    // so we just have to make sure the stack is aligned after we push the
+    // |this| + argument Values.
+    size_t numArgs = Max<size_t>(1, target->nargs());
+    uint32_t argSize = (numArgs + 1) * sizeof(Value);
+    uint32_t padding = ComputeByteAlignment(masm.framePushed() + argSize, JitStackAlignment);
+    MOZ_ASSERT(padding % sizeof(uintptr_t) == 0);
+    MOZ_ASSERT(padding < JitStackAlignment);
+    masm.reserveStack(padding);
+
+    for (size_t i = 1; i < target->nargs(); i++)
+        masm.Push(UndefinedValue());
+    masm.Push(val);
+    masm.Push(TypedOrValueRegister(MIRType::Object, AnyRegister(obj)));
+
+    masm.movePtr(ImmGCPtr(target), scratch);
+
+    descriptor = MakeFrameDescriptor(argSize + padding, JitFrame_IonICCall,
+                                     JitFrameLayout::Size());
+    masm.Push(Imm32(1)); // argc
+    masm.Push(scratch);
+    masm.Push(Imm32(descriptor));
+
+    // Check stack alignment. Add sizeof(uintptr_t) for the return address.
+    MOZ_ASSERT(((masm.framePushed() + sizeof(uintptr_t)) % JitStackAlignment) == 0);
+
+    // The setter has JIT code now and we will only discard the setter's JIT
+    // code when discarding all JIT code in the Zone, so we can assume it'll
+    // still have JIT code.
+    MOZ_ASSERT(target->hasJITCode());
+    masm.loadPtr(Address(scratch, JSFunction::offsetOfNativeOrScript()), scratch);
+    masm.loadBaselineOrIonRaw(scratch, scratch, nullptr);
+    masm.callJit(scratch);
+
+    masm.freeStack(masm.framePushed() - framePushedBefore);
+    return true;
 }
 
+typedef bool (*SetArrayLengthFn)(JSContext*, HandleObject, HandleValue, bool);
+static const VMFunction SetArrayLengthInfo =
+    FunctionInfo<SetArrayLengthFn>(SetArrayLength, "SetArrayLength");
+
 bool
 IonCacheIRCompiler::emitCallSetArrayLength()
 {
-    MOZ_CRASH("Baseline-specific op");
+    AutoSaveLiveRegisters save(*this);
+
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    bool strict = reader.readBool();
+    ConstantOrRegister val = allocator.useConstantOrRegister(masm, reader.valOperandId());
+
+    allocator.discardStack(masm);
+    prepareVMCall(masm);
+
+    masm.Push(Imm32(strict));
+    masm.Push(val);
+    masm.Push(obj);
+
+    return callVM(masm, SetArrayLengthInfo);
 }
 
+typedef bool (*ProxySetPropertyFn)(JSContext*, HandleObject, HandleId, HandleValue, bool);
+static const VMFunction ProxySetPropertyInfo =
+    FunctionInfo<ProxySetPropertyFn>(ProxySetProperty, "ProxySetProperty");
+
 bool
 IonCacheIRCompiler::emitCallProxySet()
 {
-    MOZ_CRASH("Baseline-specific op");
+    AutoSaveLiveRegisters save(*this);
+
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    ConstantOrRegister val = allocator.useConstantOrRegister(masm, reader.valOperandId());
+    jsid id = idStubField(reader.stubOffset());
+    bool strict = reader.readBool();
+
+    AutoScratchRegister scratch(allocator, masm);
+
+    allocator.discardStack(masm);
+    prepareVMCall(masm);
+
+    masm.Push(Imm32(strict));
+    masm.Push(val);
+    masm.Push(id, scratch);
+    masm.Push(obj);
+
+    return callVM(masm, ProxySetPropertyInfo);
 }
 
+typedef bool (*ProxySetPropertyByValueFn)(JSContext*, HandleObject, HandleValue, HandleValue, bool);
+static const VMFunction ProxySetPropertyByValueInfo =
+    FunctionInfo<ProxySetPropertyByValueFn>(ProxySetPropertyByValue, "ProxySetPropertyByValue");
+
 bool
 IonCacheIRCompiler::emitCallProxySetByValue()
 {
-    MOZ_CRASH("Baseline-specific op");
+    AutoSaveLiveRegisters save(*this);
+
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    ConstantOrRegister idVal = allocator.useConstantOrRegister(masm, reader.valOperandId());
+    ConstantOrRegister val = allocator.useConstantOrRegister(masm, reader.valOperandId());
+    bool strict = reader.readBool();
+
+    allocator.discardStack(masm);
+    prepareVMCall(masm);
+
+    masm.Push(Imm32(strict));
+    masm.Push(val);
+    masm.Push(idVal);
+    masm.Push(obj);
+
+    return callVM(masm, ProxySetPropertyByValueInfo);
 }
 
 bool
 IonCacheIRCompiler::emitLoadTypedObjectResult()
 {
     AutoOutputRegister output(*this);
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     AutoScratchRegister scratch1(allocator, masm);
@@ -1074,25 +1707,28 @@ IonCacheIRCompiler::emitLoadDOMExpandoVa
 
     // Load expandoAndGeneration->expando into the output Value register.
     masm.loadValue(Address(output.scratchReg(), ExpandoAndGeneration::offsetOfExpando()), output);
     return true;
 }
 
 bool
 IonIC::attachCacheIRStub(JSContext* cx, const CacheIRWriter& writer, CacheKind kind,
-                         HandleScript outerScript)
+                         IonScript* ionScript, const PropertyTypeCheckInfo* typeCheckInfo)
 {
     // We shouldn't GC or report OOM (or any other exception) here.
     AutoAssertNoPendingException aanpe(cx);
     JS::AutoCheckCannotGC nogc;
 
+    // SetProp/SetElem stubs must have non-null typeCheckInfo.
+    MOZ_ASSERT(!!typeCheckInfo == (kind == CacheKind::SetProp || kind == CacheKind::SetElem));
+
     // Do nothing if the IR generator failed or triggered a GC that invalidated
     // the script.
-    if (writer.failed() || !outerScript->hasIonScript())
+    if (writer.failed() || ionScript->invalidated())
         return false;
 
     JitZone* jitZone = cx->zone()->jitZone();
     uint32_t stubDataOffset = sizeof(IonICStub);
 
     // Try to reuse a previously-allocated CacheIRStubInfo.
     CacheIRStubKey::Lookup lookup(kind, ICStubEngine::IonIC,
                                   writer.codeStart(), writer.codeLength());
@@ -1139,17 +1775,17 @@ IonIC::attachCacheIRStub(JSContext* cx, 
     void* newStubMem = stubSpace->alloc(bytesNeeded);
     if (!newStubMem)
         return false;
 
     IonICStub* newStub = new(newStubMem) IonICStub(fallbackLabel_.raw(), stubInfo);
     writer.copyStubData(newStub->stubDataStart());
 
     JitContext jctx(cx, nullptr);
-    IonCacheIRCompiler compiler(cx, writer, this, outerScript->ionScript(), newStub);
+    IonCacheIRCompiler compiler(cx, writer, this, ionScript, newStub, typeCheckInfo);
     if (!compiler.init())
         return false;
 
     JitCode* code = compiler.compile();
     if (!code)
         return false;
 
     attachStub(newStub, code);
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -647,89 +647,16 @@ EmitLoadSlot(MacroAssembler& masm, Nativ
     } else {
         masm.loadPtr(Address(holderReg, NativeObject::offsetOfSlots()), scratchReg);
 
         Address addr(scratchReg, holder->dynamicSlotIndex(shape->slot()) * sizeof(Value));
         masm.loadTypedOrValue(addr, output);
     }
 }
 
-// Callers are expected to have already guarded on the shape of the
-// object, which guarantees the object is a DOM proxy.
-static void
-CheckDOMProxyExpandoDoesNotShadow(JSContext* cx, MacroAssembler& masm, JSObject* obj,
-                                  jsid id, Register object, Label* stubFailure)
-{
-    MOZ_ASSERT(IsCacheableDOMProxy(obj));
-
-    // Guard that the object does not have expando properties, or has an expando
-    // which is known to not have the desired property.
-
-    // For the remaining code, we need to reserve some registers to load a value.
-    // This is ugly, but unvaoidable.
-    AllocatableRegisterSet domProxyRegSet(RegisterSet::All());
-    domProxyRegSet.take(AnyRegister(object));
-    ValueOperand tempVal = domProxyRegSet.takeAnyValue();
-    masm.pushValue(tempVal);
-
-    Label failDOMProxyCheck;
-    Label domProxyOk;
-
-    Value expandoVal = GetProxyExtra(obj, GetDOMProxyExpandoSlot());
-
-    masm.loadPtr(Address(object, ProxyObject::offsetOfValues()), tempVal.scratchReg());
-    masm.loadValue(Address(tempVal.scratchReg(),
-                           ProxyObject::offsetOfExtraSlotInValues(GetDOMProxyExpandoSlot())),
-                   tempVal);
-
-    if (!expandoVal.isObject() && !expandoVal.isUndefined()) {
-        masm.branchTestValue(Assembler::NotEqual, tempVal, expandoVal, &failDOMProxyCheck);
-
-        ExpandoAndGeneration* expandoAndGeneration = (ExpandoAndGeneration*)expandoVal.toPrivate();
-        masm.movePtr(ImmPtr(expandoAndGeneration), tempVal.scratchReg());
-
-        masm.branch64(Assembler::NotEqual,
-                      Address(tempVal.scratchReg(),
-                              ExpandoAndGeneration::offsetOfGeneration()),
-                      Imm64(expandoAndGeneration->generation),
-                      &failDOMProxyCheck);
-
-        expandoVal = expandoAndGeneration->expando;
-        masm.loadValue(Address(tempVal.scratchReg(),
-                               ExpandoAndGeneration::offsetOfExpando()),
-                       tempVal);
-    }
-
-    // If the incoming object does not have an expando object then we're sure we're not
-    // shadowing.
-    masm.branchTestUndefined(Assembler::Equal, tempVal, &domProxyOk);
-
-    if (expandoVal.isObject()) {
-        MOZ_ASSERT(!expandoVal.toObject().as<NativeObject>().contains(cx, id));
-
-        // Reference object has an expando object that doesn't define the name. Check that
-        // the incoming object has an expando object with the same shape.
-        masm.branchTestObject(Assembler::NotEqual, tempVal, &failDOMProxyCheck);
-        masm.extractObject(tempVal, tempVal.scratchReg());
-        masm.branchPtr(Assembler::Equal,
-                       Address(tempVal.scratchReg(), ShapedObject::offsetOfShape()),
-                       ImmGCPtr(expandoVal.toObject().as<NativeObject>().lastProperty()),
-                       &domProxyOk);
-    }
-
-    // Failure case: restore the tempVal registers and jump to failures.
-    masm.bind(&failDOMProxyCheck);
-    masm.popValue(tempVal);
-    masm.jump(stubFailure);
-
-    // Success case: restore the tempval and proceed.
-    masm.bind(&domProxyOk);
-    masm.popValue(tempVal);
-}
-
 static void
 GenerateReadSlot(JSContext* cx, IonScript* ion, MacroAssembler& masm,
                  IonCache::StubAttacher& attacher, MaybeCheckTDZ checkTDZ,
                  JSObject* obj, JSObject* holder, PropertyResult prop, Register object,
                  TypedOrValueRegister output, Label* failures = nullptr)
 {
     // If there's a single jump to |failures|, we can patch the shape guard
     // jump directly. Otherwise, jump to the end of the stub, so there's a
@@ -1095,103 +1022,16 @@ GenerateCallGetter(JSContext* cx, IonScr
         masm.pop(object);
     }
     masm.bind(failures);
     attacher.jumpNextStub(masm);
 
     return true;
 }
 
-static void
-EmitIdGuard(MacroAssembler& masm, jsid id, TypedOrValueRegister idReg, Register objReg,
-            Register scratchReg, Label* failures)
-{
-    MOZ_ASSERT(JSID_IS_STRING(id) || JSID_IS_SYMBOL(id));
-
-    MOZ_ASSERT(idReg.type() == MIRType::String ||
-               idReg.type() == MIRType::Symbol ||
-               idReg.type() == MIRType::Value);
-
-    Register payloadReg;
-    if (idReg.type() == MIRType::Value) {
-        ValueOperand val = idReg.valueReg();
-        if (JSID_IS_SYMBOL(id)) {
-            masm.branchTestSymbol(Assembler::NotEqual, val, failures);
-        } else {
-            MOZ_ASSERT(JSID_IS_STRING(id));
-            masm.branchTestString(Assembler::NotEqual, val, failures);
-        }
-        masm.unboxNonDouble(val, scratchReg);
-        payloadReg = scratchReg;
-    } else {
-        payloadReg = idReg.typedReg().gpr();
-    }
-
-    if (JSID_IS_SYMBOL(id)) {
-        // For symbols, we can just do a pointer comparison.
-        masm.branchPtr(Assembler::NotEqual, payloadReg, ImmGCPtr(JSID_TO_SYMBOL(id)), failures);
-    } else {
-        PropertyName* name = JSID_TO_ATOM(id)->asPropertyName();
-
-        Label equal;
-        masm.branchPtr(Assembler::Equal, payloadReg, ImmGCPtr(name), &equal);
-
-        // The pointers are not equal, so if the input string is also an atom it
-        // must be a different string.
-        masm.branchTest32(Assembler::NonZero, Address(payloadReg, JSString::offsetOfFlags()),
-                          Imm32(JSString::ATOM_BIT), failures);
-
-        // Check the length.
-        masm.branch32(Assembler::NotEqual, Address(payloadReg, JSString::offsetOfLength()),
-                      Imm32(name->length()), failures);
-
-        // We have a non-atomized string with the same length. For now call a helper
-        // function to do the comparison.
-        LiveRegisterSet volatileRegs(RegisterSet::Volatile());
-        masm.PushRegsInMask(volatileRegs);
-
-        if (!volatileRegs.has(objReg))
-            masm.push(objReg);
-
-        masm.setupUnalignedABICall(objReg);
-        masm.movePtr(ImmGCPtr(name), objReg);
-        masm.passABIArg(objReg);
-        masm.passABIArg(payloadReg);
-        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, EqualStringsHelper));
-        masm.mov(ReturnReg, scratchReg);
-
-        if (!volatileRegs.has(objReg))
-            masm.pop(objReg);
-
-        LiveRegisterSet ignore;
-        ignore.add(scratchReg);
-        masm.PopRegsInMaskIgnore(volatileRegs, ignore);
-
-        masm.branchIfFalseBool(scratchReg, failures);
-        masm.bind(&equal);
-    }
-}
-
-void
-SetPropertyIC::emitIdGuard(MacroAssembler& masm, jsid id, Label* fail)
-{
-    if (this->id().constant())
-        return;
-
-    EmitIdGuard(masm, id, this->id().reg(), object(), temp(), fail);
-}
-
-static void
-PushObjectOpResult(MacroAssembler& masm)
-{
-    static_assert(sizeof(ObjectOpResult) == sizeof(uintptr_t),
-                  "ObjectOpResult size must match size reserved by masm.Push() here");
-    masm.Push(ImmWord(ObjectOpResult::Uninitialized));
-}
-
 bool
 jit::ValueToNameOrSymbolId(JSContext* cx, HandleValue idval, MutableHandleId id,
                            bool* nameOrSymbol)
 {
     *nameOrSymbol = false;
 
     if (!idval.isString() && !idval.isSymbol())
         return true;
@@ -1224,98 +1064,16 @@ IonCache::disable()
 void
 IonCache::reset(ReprotectCode reprotect)
 {
     this->stubCount_ = 0;
     PatchJump(initialJump_, fallbackLabel_, reprotect);
     lastJump_ = initialJump_;
 }
 
-// Jump to failure if a value being written is not a property for obj/id.
-static void
-CheckTypeSetForWrite(MacroAssembler& masm, JSObject* obj, jsid id,
-                     Register scratch, const ConstantOrRegister& value, Label* failure)
-{
-    TypedOrValueRegister valReg = value.reg();
-    ObjectGroup* group = obj->group();
-    MOZ_ASSERT(!group->unknownProperties());
-
-    HeapTypeSet* propTypes = group->maybeGetProperty(id);
-    MOZ_ASSERT(propTypes);
-
-    // guardTypeSet can read from type sets without triggering read barriers.
-    TypeSet::readBarrier(propTypes);
-
-    masm.guardTypeSet(valReg, propTypes, BarrierKind::TypeSet, scratch, failure);
-}
-
-static void
-GenerateSetSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& attacher,
-                JSObject* obj, Shape* shape, Register object, Register tempReg,
-                const ConstantOrRegister& value, bool needsTypeBarrier, bool checkTypeset,
-                Label* failures)
-{
-    TestMatchingReceiver(masm, attacher, object, obj, failures, needsTypeBarrier);
-
-    // Guard that the incoming value is in the type set for the property
-    // if a type barrier is required.
-    if (checkTypeset) {
-        MOZ_ASSERT(needsTypeBarrier);
-        CheckTypeSetForWrite(masm, obj, shape->propid(), tempReg, value, failures);
-    }
-
-    NativeObject::slotsSizeMustNotOverflow();
-
-    if (obj->is<UnboxedPlainObject>()) {
-        obj = obj->as<UnboxedPlainObject>().maybeExpando();
-        masm.loadPtr(Address(object, UnboxedPlainObject::offsetOfExpando()), tempReg);
-        object = tempReg;
-    }
-
-    if (obj->as<NativeObject>().isFixedSlot(shape->slot())) {
-        Address addr(object, NativeObject::getFixedSlotOffset(shape->slot()));
-
-        if (cx->zone()->needsIncrementalBarrier())
-            masm.callPreBarrier(addr, MIRType::Value);
-
-        masm.storeConstantOrRegister(value, addr);
-    } else {
-        masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), tempReg);
-
-        Address addr(tempReg, obj->as<NativeObject>().dynamicSlotIndex(shape->slot()) * sizeof(Value));
-
-        if (cx->zone()->needsIncrementalBarrier())
-            masm.callPreBarrier(addr, MIRType::Value);
-
-        masm.storeConstantOrRegister(value, addr);
-    }
-
-    attacher.jumpRejoin(masm);
-
-    masm.bind(failures);
-    attacher.jumpNextStub(masm);
-}
-
-bool
-SetPropertyIC::attachSetSlot(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                             HandleObject obj, HandleShape shape, bool checkTypeset)
-{
-    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
-    StubAttacher attacher(*this);
-
-    Label failures;
-    emitIdGuard(masm, shape->propid(), &failures);
-
-    GenerateSetSlot(cx, masm, attacher, obj, shape, object(), temp(), value(), needsTypeBarrier(),
-                    checkTypeset, &failures);
-
-    return linkAndAttachStub(cx, masm, attacher, ion, "setting",
-                             JS::TrackedOutcome::ICSetPropStub_Slot);
-}
-
 bool
 jit::IsCacheableSetPropCallNative(JSObject* obj, JSObject* holder, Shape* shape)
 {
     if (!shape || !IsCacheableProtoChainForIonOrCacheIR(obj, holder))
         return false;
 
     if (!shape->hasSetterValue())
         return false;
@@ -1357,1366 +1115,19 @@ jit::IsCacheableSetPropCallScripted(JSOb
         if (isTemporarilyUnoptimizable)
             *isTemporarilyUnoptimizable = true;
         return false;
     }
 
     return true;
 }
 
-static bool
-IsCacheableSetPropCallPropertyOp(HandleObject obj, HandleObject holder, HandleShape shape)
-{
-    if (!shape)
-        return false;
-
-    if (!IsCacheableProtoChainForIonOrCacheIR(obj, holder))
-        return false;
-
-    if (shape->hasSlot())
-        return false;
-
-    if (shape->hasDefaultSetter())
-        return false;
-
-    if (shape->hasSetterValue())
-        return false;
-
-    // Despite the vehement claims of Shape.h that writable() is only relevant
-    // for data descriptors, some SetterOps care desperately about its
-    // value. The flag should be always true, apart from these rare instances.
-    if (!shape->writable())
-        return false;
-
-    return true;
-}
-
-static bool
-ReportStrictErrorOrWarning(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool strict,
-                           JS::ObjectOpResult& result)
-{
-    return result.reportStrictErrorOrWarning(cx, obj, id, strict);
-}
-
-template <class FrameLayout>
 void
-EmitObjectOpResultCheck(MacroAssembler& masm, Label* failure, bool strict,
-                        Register scratchReg,
-                        Register argJSContextReg,
-                        Register argObjReg,
-                        Register argIdReg,
-                        Register argStrictReg,
-                        Register argResultReg)
-{
-    // if (!result) {
-    Label noStrictError;
-    masm.branch32(Assembler::Equal,
-                  Address(masm.getStackPointer(),
-                          FrameLayout::offsetOfObjectOpResult()),
-                  Imm32(ObjectOpResult::OkCode),
-                  &noStrictError);
-
-    //     if (!ReportStrictErrorOrWarning(cx, obj, id, strict, &result))
-    //         goto failure;
-    masm.loadJSContext(argJSContextReg);
-    masm.computeEffectiveAddress(
-        Address(masm.getStackPointer(), FrameLayout::offsetOfObject()),
-        argObjReg);
-    masm.computeEffectiveAddress(
-        Address(masm.getStackPointer(), FrameLayout::offsetOfId()),
-        argIdReg);
-    masm.move32(Imm32(strict), argStrictReg);
-    masm.computeEffectiveAddress(
-        Address(masm.getStackPointer(), FrameLayout::offsetOfObjectOpResult()),
-        argResultReg);
-    masm.setupUnalignedABICall(scratchReg);
-    masm.passABIArg(argJSContextReg);
-    masm.passABIArg(argObjReg);
-    masm.passABIArg(argIdReg);
-    masm.passABIArg(argStrictReg);
-    masm.passABIArg(argResultReg);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, ReportStrictErrorOrWarning));
-    masm.branchIfFalseBool(ReturnReg, failure);
-
-    // }
-    masm.bind(&noStrictError);
-}
-
-static bool
-EmitCallProxySet(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& attacher,
-                 HandleId propId, LiveRegisterSet liveRegs, Register object,
-                 const ConstantOrRegister& value, void* returnAddr, bool strict)
-{
-    MacroAssembler::AfterICSaveLive aic = masm.icSaveLive(liveRegs);
-
-    // Remaining registers should be free, but we still need to use |object| so
-    // leave it alone.
-    //
-    // WARNING: We do not take() the register used by |value|, if any, so
-    // regSet is going to re-allocate it. Hence the emitted code must not touch
-    // any of the registers allocated from regSet until after the last use of
-    // |value|. (We can't afford to take it, either, because x86.)
-    AllocatableRegisterSet regSet(RegisterSet::All());
-    regSet.take(AnyRegister(object));
-
-    // ProxySetProperty(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v,
-    //                  bool strict);
-    Register argJSContextReg = regSet.takeAnyGeneral();
-    Register argProxyReg     = regSet.takeAnyGeneral();
-    Register argIdReg        = regSet.takeAnyGeneral();
-    Register argValueReg     = regSet.takeAnyGeneral();
-    Register argStrictReg    = regSet.takeAnyGeneral();
-
-    Register scratch         = regSet.takeAnyGeneral();
-
-    // Push stubCode for marking.
-    attacher.pushStubCodePointer(masm);
-
-    // Push args on stack so we can take pointers to make handles.
-    // Push value before touching any other registers (see WARNING above).
-    masm.Push(value);
-    masm.moveStackPtrTo(argValueReg);
-
-    masm.move32(Imm32(strict), argStrictReg);
-
-    masm.Push(propId, scratch);
-    masm.moveStackPtrTo(argIdReg);
-
-    // Push object.
-    masm.Push(object);
-    masm.moveStackPtrTo(argProxyReg);
-
-    masm.loadJSContext(argJSContextReg);
-
-    if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
-        return false;
-    masm.enterFakeExitFrame(scratch, IonOOLProxyExitFrameLayoutToken);
-
-    // Make the call.
-    masm.setupUnalignedABICall(scratch);
-    masm.passABIArg(argJSContextReg);
-    masm.passABIArg(argProxyReg);
-    masm.passABIArg(argIdReg);
-    masm.passABIArg(argValueReg);
-    masm.passABIArg(argStrictReg);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, ProxySetProperty));
-
-    // Test for error.
-    masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
-
-    // masm.leaveExitFrame & pop locals
-    masm.adjustStack(IonOOLProxyExitFrameLayout::Size());
-
-    masm.icRestoreLive(liveRegs, aic);
-    return true;
-}
-
-bool
-SetPropertyIC::attachGenericProxy(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                                  HandleId id, void* returnAddr)
-{
-    MOZ_ASSERT(!hasGenericProxyStub());
-
-    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
-    StubAttacher attacher(*this);
-
-    Label failures;
-    emitIdGuard(masm, id, &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);
-    }
-
-    if (!EmitCallProxySet(cx, masm, attacher, id, liveRegs_, object(), value(),
-                          returnAddr, strict()))
-    {
-        return false;
-    }
-
-    attacher.jumpRejoin(masm);
-
-    masm.bind(&failures);
-    attacher.jumpNextStub(masm);
-
-    MOZ_ASSERT(!hasGenericProxyStub_);
-    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, HandleId id, void* returnAddr)
-{
-    MOZ_ASSERT(IsCacheableDOMProxy(obj));
-
-    Label failures;
-    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
-    StubAttacher attacher(*this);
-
-    emitIdGuard(masm, id, &failures);
-
-    // Guard on the shape of the object.
-    masm.branchPtr(Assembler::NotEqual,
-                   Address(object(), ShapedObject::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.
-
-    if (!EmitCallProxySet(cx, masm, attacher, id, liveRegs_, object(),
-                          value(), returnAddr, strict()))
-    {
-        return false;
-    }
-
-    // Success.
-    attacher.jumpRejoin(masm);
-
-    // Failure.
-    masm.bind(&failures);
-    attacher.jumpNextStub(masm);
-
-    return linkAndAttachStub(cx, masm, attacher, ion, "DOM proxy shadowed set",
-                             JS::TrackedOutcome::ICSetPropStub_DOMProxyShadowed);
-}
-
-static bool
-GenerateCallSetter(JSContext* cx, IonScript* ion, MacroAssembler& masm,
-                   IonCache::StubAttacher& attacher, HandleObject obj, HandleObject holder,
-                   HandleShape shape, bool strict, Register object, Register tempReg,
-                   const ConstantOrRegister& value, Label* failure, LiveRegisterSet liveRegs,
-                   void* returnAddr)
-{
-    // Generate prototype guards if needed.
-    {
-        // Generate prototype/shape guards.
-        if (obj != holder)
-            GeneratePrototypeGuards(cx, ion, masm, obj, holder, object, tempReg, failure);
-
-        masm.movePtr(ImmGCPtr(holder), tempReg);
-        masm.branchPtr(Assembler::NotEqual,
-                       Address(tempReg, ShapedObject::offsetOfShape()),
-                       ImmGCPtr(holder->as<NativeObject>().lastProperty()),
-                       failure);
-    }
-
-    // Good to go for invoking setter.
-
-    MacroAssembler::AfterICSaveLive aic = masm.icSaveLive(liveRegs);
-
-    // Remaining registers should basically be free, but we need to use |object| still
-    // so leave it alone.  And of course we need our value, if it's not a constant.
-    AllocatableRegisterSet regSet(RegisterSet::All());
-    if (!value.constant())
-        regSet.take(value.reg());
-    bool valueAliasesObject = !regSet.has(object);
-    if (!valueAliasesObject)
-        regSet.take(object);
-
-    regSet.take(tempReg);
-
-    // This is a slower stub path, and we're going to be doing a call anyway.  Don't need
-    // to try so hard to not use the stack.  Scratch regs are just taken from the register
-    // set not including the input, current value saved on the stack, and restored when
-    // we're done with it.
-    //
-    // Be very careful not to use any of these before value is pushed, since they
-    // might shadow.
-
-    if (IsCacheableSetPropCallNative(obj, holder, shape)) {
-        Register argJSContextReg = regSet.takeAnyGeneral();
-        Register argVpReg        = regSet.takeAnyGeneral();
-
-        MOZ_ASSERT(shape->hasSetterValue() && shape->setterObject() &&
-                   shape->setterObject()->is<JSFunction>());
-        JSFunction* target = &shape->setterObject()->as<JSFunction>();
-
-        MOZ_ASSERT(target->isNative());
-
-        Register argUintNReg = regSet.takeAnyGeneral();
-
-        // Set up the call:
-        //  bool (*)(JSContext*, unsigned, Value* vp)
-        // vp[0] is callee/outparam
-        // vp[1] is |this|
-        // vp[2] is the value
-
-        // Build vp and move the base into argVpReg.
-        masm.Push(value);
-        masm.Push(TypedOrValueRegister(MIRType::Object, AnyRegister(object)));
-        masm.Push(ObjectValue(*target));
-        masm.moveStackPtrTo(argVpReg);
-
-        // Preload other regs
-        masm.loadJSContext(argJSContextReg);
-        masm.move32(Imm32(1), argUintNReg);
-
-        // Push data for GC marking
-        masm.Push(argUintNReg);
-        attacher.pushStubCodePointer(masm);
-
-        if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
-            return false;
-        masm.enterFakeExitFrame(tempReg, IonOOLNativeExitFrameLayoutToken);
-
-        // Make the call
-        masm.setupUnalignedABICall(tempReg);
-        masm.passABIArg(argJSContextReg);
-        masm.passABIArg(argUintNReg);
-        masm.passABIArg(argVpReg);
-        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, target->native()));
-
-        // Test for failure.
-        masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
-
-        // masm.leaveExitFrame & pop locals.
-        masm.adjustStack(IonOOLNativeExitFrameLayout::Size(1));
-    } else if (IsCacheableSetPropCallPropertyOp(obj, holder, shape)) {
-        // We can't take all our registers up front, because on x86 we need 2
-        // for the value, one for scratch, 5 for the arguments, which makes 8,
-        // but we only have 7 to work with.  So only grab the ones we need
-        // before we push value and release its reg back into the set.
-        Register argResultReg = regSet.takeAnyGeneral();
-
-        SetterOp target = shape->setterOp();
-        MOZ_ASSERT(target);
-
-        // JSSetterOp: bool fn(JSContext* cx, HandleObject obj,
-        //                     HandleId id, HandleValue value, ObjectOpResult& result);
-
-        // First, allocate an ObjectOpResult on the stack. We push this before
-        // the stubCode pointer in order to match the layout of
-        // IonOOLSetterOpExitFrameLayout.
-        PushObjectOpResult(masm);
-        masm.moveStackPtrTo(argResultReg);
-
-        attacher.pushStubCodePointer(masm);
-
-        // Push args on stack so we can take pointers to make handles.
-        if (value.constant()) {
-            masm.Push(value.value());
-        } else {
-            masm.Push(value.reg());
-            if (!valueAliasesObject)
-                regSet.add(value.reg());
-        }
-
-        // OK, now we can grab our remaining registers and grab the pointer to
-        // what we just pushed into one of them.
-        Register argJSContextReg = regSet.takeAnyGeneral();
-        Register argValueReg     = regSet.takeAnyGeneral();
-        // We can just reuse the "object" register for argObjReg
-        Register argObjReg       = object;
-        Register argIdReg        = regSet.takeAnyGeneral();
-        masm.moveStackPtrTo(argValueReg);
-
-        // push canonical jsid from shape instead of propertyname.
-        masm.Push(shape->propid(), argIdReg);
-        masm.moveStackPtrTo(argIdReg);
-
-        masm.Push(object);
-        masm.moveStackPtrTo(argObjReg);
-
-        masm.loadJSContext(argJSContextReg);
-
-        if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
-            return false;
-        masm.enterFakeExitFrame(tempReg, IonOOLSetterOpExitFrameLayoutToken);
-
-        // Make the call.
-        masm.setupUnalignedABICall(tempReg);
-        masm.passABIArg(argJSContextReg);
-        masm.passABIArg(argObjReg);
-        masm.passABIArg(argIdReg);
-        masm.passABIArg(argValueReg);
-        masm.passABIArg(argResultReg);
-        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, target));
-
-        // Test for error.
-        masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
-
-        // Test for strict failure. We emit the check even in non-strict mode
-        // in order to pick up the warning if extraWarnings is enabled.
-        EmitObjectOpResultCheck<IonOOLSetterOpExitFrameLayout>(masm, masm.exceptionLabel(),
-                                                               strict, tempReg,
-                                                               argJSContextReg, argObjReg,
-                                                               argIdReg, argValueReg,
-                                                               argResultReg);
-
-        // masm.leaveExitFrame & pop locals.
-        masm.adjustStack(IonOOLSetterOpExitFrameLayout::Size());
-    } else {
-        MOZ_ASSERT(IsCacheableSetPropCallScripted(obj, holder, shape));
-
-        JSFunction* target = &shape->setterValue().toObject().as<JSFunction>();
-        uint32_t framePushedBefore = masm.framePushed();
-
-        // Construct IonICCallFrameLayout.
-        uint32_t descriptor = MakeFrameDescriptor(masm.framePushed(), JitFrame_IonJS,
-                                                  IonICCallFrameLayout::Size());
-        attacher.pushStubCodePointer(masm);
-        masm.Push(Imm32(descriptor));
-        masm.Push(ImmPtr(returnAddr));
-
-        // The JitFrameLayout pushed below will be aligned to JitStackAlignment,
-        // so we just have to make sure the stack is aligned after we push the
-        // |this| + argument Values.
-        uint32_t numArgs = Max(size_t(1), target->nargs());
-        uint32_t argSize = (numArgs + 1) * sizeof(Value);
-        uint32_t padding = ComputeByteAlignment(masm.framePushed() + argSize, JitStackAlignment);
-        MOZ_ASSERT(padding % sizeof(uintptr_t) == 0);
-        MOZ_ASSERT(padding < JitStackAlignment);
-        masm.reserveStack(padding);
-
-        for (size_t i = 1; i < target->nargs(); i++)
-            masm.Push(UndefinedValue());
-        masm.Push(value);
-        masm.Push(TypedOrValueRegister(MIRType::Object, AnyRegister(object)));
-
-        masm.movePtr(ImmGCPtr(target), tempReg);
-
-        descriptor = MakeFrameDescriptor(argSize + padding, JitFrame_IonICCall,
-                                         JitFrameLayout::Size());
-        masm.Push(Imm32(1)); // argc
-        masm.Push(tempReg);
-        masm.Push(Imm32(descriptor));
-
-        // Check stack alignment. Add sizeof(uintptr_t) for the return address.
-        MOZ_ASSERT(((masm.framePushed() + sizeof(uintptr_t)) % JitStackAlignment) == 0);
-
-        // The setter has JIT code now and we will only discard the setter's JIT
-        // code when discarding all JIT code in the Zone, so we can assume it'll
-        // still have JIT code.
-        MOZ_ASSERT(target->hasJITCode());
-        masm.loadPtr(Address(tempReg, JSFunction::offsetOfNativeOrScript()), tempReg);
-        masm.loadBaselineOrIonRaw(tempReg, tempReg, nullptr);
-        masm.callJit(tempReg);
-
-        masm.freeStack(masm.framePushed() - framePushedBefore);
-    }
-
-    masm.icRestoreLive(liveRegs, aic);
-    return true;
-}
-
-static bool
-IsCacheableDOMProxyUnshadowedSetterCall(JSContext* cx, HandleObject obj, HandleId id,
-                                        MutableHandleObject holder, MutableHandleShape shape)
-{
-    MOZ_ASSERT(IsCacheableDOMProxy(obj));
-
-    RootedObject checkObj(cx, obj->staticPrototype());
-    if (!checkObj)
-        return false;
-
-    PropertyResult prop;
-    if (!LookupPropertyPure(cx, obj, id, holder.address(), &prop))
-        return false;
-
-    if (!holder || !holder->isNative())
-        return false;
-
-    shape.set(prop.shape());
-    return IsCacheableSetPropCallNative(checkObj, holder, shape) ||
-           IsCacheableSetPropCallPropertyOp(checkObj, holder, shape) ||
-           IsCacheableSetPropCallScripted(checkObj, holder, shape);
-}
-
-bool
-SetPropertyIC::attachDOMProxyUnshadowed(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                                        HandleObject obj, HandleId id, void* returnAddr)
-{
-    MOZ_ASSERT(IsCacheableDOMProxy(obj));
-
-    Label failures;
-    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
-    StubAttacher attacher(*this);
-
-    emitIdGuard(masm, id, &failures);
-
-    // Guard on the shape of the object.
-    masm.branchPtr(Assembler::NotEqual,
-                   Address(object(), ShapedObject::offsetOfShape()),
-                   ImmGCPtr(obj->maybeShape()), &failures);
-
-    // Guard that our expando object hasn't started shadowing this property.
-    CheckDOMProxyExpandoDoesNotShadow(cx, masm, obj, id, object(), &failures);
-
-    RootedObject holder(cx);
-    RootedShape shape(cx);
-    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().
-        if (!EmitCallProxySet(cx, masm, attacher, id, liveRegs_, object(),
-                            value(), returnAddr, strict()))
-        {
-            return false;
-        }
-    }
-
-    // Success.
-    attacher.jumpRejoin(masm);
-
-    // Failure.
-    masm.bind(&failures);
-    attacher.jumpNextStub(masm);
-
-    return linkAndAttachStub(cx, masm, attacher, ion, "DOM proxy unshadowed set",
-                             JS::TrackedOutcome::ICSetPropStub_DOMProxyUnshadowed);
-}
-
-bool
-SetPropertyIC::attachCallSetter(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                                HandleObject obj, HandleObject holder, HandleShape shape,
-                                void* returnAddr)
-{
-    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
-    StubAttacher attacher(*this);
-
-    Label failure;
-    emitIdGuard(masm, shape->propid(), &failure);
-    TestMatchingReceiver(masm, attacher, object(), obj, &failure);
-
-    if (!GenerateCallSetter(cx, ion, masm, attacher, obj, holder, shape, strict(),
-                            object(), temp(), value(), &failure, liveRegs_, returnAddr))
-    {
-        return false;
-    }
-
-    // Rejoin jump.
-    attacher.jumpRejoin(masm);
-
-    // Jump to next stub.
-    masm.bind(&failure);
-    attacher.jumpNextStub(masm);
-
-    return linkAndAttachStub(cx, masm, attacher, ion, "setter call",
-                             JS::TrackedOutcome::ICSetPropStub_CallSetter);
-}
-
-static void
-GenerateAddSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& attacher,
-                JSObject* obj, Shape* oldShape, ObjectGroup* oldGroup,
-                Register object, Register tempReg, const ConstantOrRegister& value,
-                bool checkTypeset, Label* failures)
-{
-    // Use a modified version of TestMatchingReceiver that uses the old shape and group.
-    masm.branchTestObjGroup(Assembler::NotEqual, object, oldGroup, failures);
-    if (obj->maybeShape()) {
-        masm.branchTestObjShape(Assembler::NotEqual, object, oldShape, failures);
-    } else {
-        MOZ_ASSERT(obj->is<UnboxedPlainObject>());
-
-        Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando());
-        masm.branchPtr(Assembler::Equal, expandoAddress, ImmWord(0), failures);
-
-        masm.loadPtr(expandoAddress, tempReg);
-        masm.branchTestObjShape(Assembler::NotEqual, tempReg, oldShape, failures);
-    }
-
-    Shape* newShape = obj->maybeShape();
-    if (!newShape)
-        newShape = obj->as<UnboxedPlainObject>().maybeExpando()->lastProperty();
-
-    // Guard that the incoming value is in the type set for the property
-    // if a type barrier is required.
-    if (checkTypeset)
-        CheckTypeSetForWrite(masm, obj, newShape->propid(), tempReg, value, failures);
-
-    // Guard shapes along prototype chain.
-    JSObject* proto = obj->staticPrototype();
-    Register protoReg = tempReg;
-    bool first = true;
-    while (proto) {
-        Shape* protoShape = proto->as<NativeObject>().lastProperty();
-
-        // Load next prototype.
-        masm.loadObjProto(first ? object : protoReg, protoReg);
-        first = false;
-
-        // Ensure that its shape matches.
-        masm.branchTestObjShape(Assembler::NotEqual, protoReg, protoShape, failures);
-
-        proto = proto->staticPrototype();
-    }
-
-    // Call a stub to (re)allocate dynamic slots, if necessary.
-    uint32_t newNumDynamicSlots = obj->is<UnboxedPlainObject>()
-                                  ? obj->as<UnboxedPlainObject>().maybeExpando()->numDynamicSlots()
-                                  : obj->as<NativeObject>().numDynamicSlots();
-    if (NativeObject::dynamicSlotsCount(oldShape) != newNumDynamicSlots) {
-        AllocatableRegisterSet regs(RegisterSet::Volatile());
-        LiveRegisterSet save(regs.asLiveSet());
-        masm.PushRegsInMask(save);
-
-        // Get 2 temp registers, without clobbering the object register.
-        regs.takeUnchecked(object);
-        Register temp1 = regs.takeAnyGeneral();
-        Register temp2 = regs.takeAnyGeneral();
-
-        if (obj->is<UnboxedPlainObject>()) {
-            // Pass the expando object to the stub.
-            masm.Push(object);
-            masm.loadPtr(Address(object, UnboxedPlainObject::offsetOfExpando()), object);
-        }
-
-        masm.setupUnalignedABICall(temp1);
-        masm.loadJSContext(temp1);
-        masm.passABIArg(temp1);
-        masm.passABIArg(object);
-        masm.move32(Imm32(newNumDynamicSlots), temp2);
-        masm.passABIArg(temp2);
-        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, NativeObject::growSlotsDontReportOOM));
-
-        // Branch on ReturnReg before restoring volatile registers, so
-        // ReturnReg isn't clobbered.
-        uint32_t framePushedAfterCall = masm.framePushed();
-        Label allocFailed, allocDone;
-        masm.branchIfFalseBool(ReturnReg, &allocFailed);
-        masm.jump(&allocDone);
-
-        masm.bind(&allocFailed);
-        if (obj->is<UnboxedPlainObject>())
-            masm.Pop(object);
-        masm.PopRegsInMask(save);
-        masm.jump(failures);
-
-        masm.bind(&allocDone);
-        masm.setFramePushed(framePushedAfterCall);
-        if (obj->is<UnboxedPlainObject>())
-            masm.Pop(object);
-        masm.PopRegsInMask(save);
-    }
-
-    bool popObject = false;
-
-    if (obj->is<UnboxedPlainObject>()) {
-        masm.push(object);
-        popObject = true;
-        obj = obj->as<UnboxedPlainObject>().maybeExpando();
-        masm.loadPtr(Address(object, UnboxedPlainObject::offsetOfExpando()), object);
-    }
-
-    // Write the object or expando object's new shape.
-    Address shapeAddr(object, ShapedObject::offsetOfShape());
-    if (cx->zone()->needsIncrementalBarrier())
-        masm.callPreBarrier(shapeAddr, MIRType::Shape);
-    masm.storePtr(ImmGCPtr(newShape), shapeAddr);
-
-    if (oldGroup != obj->group()) {
-        MOZ_ASSERT(!obj->is<UnboxedPlainObject>());
-
-        // Changing object's group from a partially to fully initialized group,
-        // per the acquired properties analysis. Only change the group if the
-        // old group still has a newScript.
-        Label noTypeChange, skipPop;
-
-        masm.loadPtr(Address(object, JSObject::offsetOfGroup()), tempReg);
-        masm.branchPtr(Assembler::Equal,
-                       Address(tempReg, ObjectGroup::offsetOfAddendum()),
-                       ImmWord(0),
-                       &noTypeChange);
-
-        Address groupAddr(object, JSObject::offsetOfGroup());
-        if (cx->zone()->needsIncrementalBarrier())
-            masm.callPreBarrier(groupAddr, MIRType::ObjectGroup);
-        masm.storePtr(ImmGCPtr(obj->group()), groupAddr);
-
-        masm.bind(&noTypeChange);
-    }
-
-    // Set the value on the object. Since this is an add, obj->lastProperty()
-    // must be the shape of the property we are adding.
-    NativeObject::slotsSizeMustNotOverflow();
-    if (obj->as<NativeObject>().isFixedSlot(newShape->slot())) {
-        Address addr(object, NativeObject::getFixedSlotOffset(newShape->slot()));
-        masm.storeConstantOrRegister(value, addr);
-    } else {
-        masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), tempReg);
-
-        Address addr(tempReg, obj->as<NativeObject>().dynamicSlotIndex(newShape->slot()) * sizeof(Value));
-        masm.storeConstantOrRegister(value, addr);
-    }
-
-    if (popObject)
-        masm.pop(object);
-
-    // Success.
-    attacher.jumpRejoin(masm);
-
-    // Failure.
-    masm.bind(failures);
-
-    attacher.jumpNextStub(masm);
-}
-
-bool
-SetPropertyIC::attachAddSlot(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                             HandleObject obj, HandleId id, HandleShape oldShape,
-                             HandleObjectGroup oldGroup, bool checkTypeset)
-{
-    MOZ_ASSERT_IF(!needsTypeBarrier(), !checkTypeset);
-
-    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
-    StubAttacher attacher(*this);
-
-    Label failures;
-    emitIdGuard(masm, id, &failures);
-
-    GenerateAddSlot(cx, masm, attacher, obj, oldShape, oldGroup, object(), temp(), value(),
-                    checkTypeset, &failures);
-    return linkAndAttachStub(cx, masm, attacher, ion, "adding",
-                             JS::TrackedOutcome::ICSetPropStub_AddSlot);
-}
-
-static bool
-CanInlineSetPropTypeCheck(JSObject* obj, jsid id, const ConstantOrRegister& val,
-                          bool* checkTypeset)
-{
-    bool shouldCheck = false;
-    ObjectGroup* group = obj->group();
-    if (!group->unknownProperties()) {
-        HeapTypeSet* propTypes = group->maybeGetProperty(id);
-        if (!propTypes)
-            return false;
-        if (!propTypes->unknown()) {
-            if (obj->isSingleton() && !propTypes->nonConstantProperty())
-                return false;
-            shouldCheck = true;
-            if (val.constant()) {
-                // If the input is a constant, then don't bother if the barrier will always fail.
-                if (!propTypes->hasType(TypeSet::GetValueType(val.value())))
-                    return false;
-                shouldCheck = false;
-            } else {
-                TypedOrValueRegister reg = val.reg();
-                // We can do the same trick as above for primitive types of specialized registers.
-                // TIs handling of objects is complicated enough to warrant a runtime
-                // check, as we can't statically handle the case where the typeset
-                // contains the specific object, but doesn't have ANYOBJECT set.
-                if (reg.hasTyped() && reg.type() != MIRType::Object) {
-                    JSValueType valType = ValueTypeFromMIRType(reg.type());
-                    if (!propTypes->hasType(TypeSet::PrimitiveType(valType)))
-                        return false;
-                    shouldCheck = false;
-                }
-            }
-        }
-    }
-
-    *checkTypeset = shouldCheck;
-    return true;
-}
-
-static bool
-IsPropertySetInlineable(NativeObject* obj, HandleId id, MutableHandleShape pshape,
-                        const ConstantOrRegister& val, bool needsTypeBarrier, bool* checkTypeset)
-{
-    // CanInlineSetPropTypeCheck assumes obj has a non-lazy group.
-    MOZ_ASSERT(!obj->hasLazyGroup());
-
-    // Do a pure non-proto chain climbing lookup. See note in
-    // CanAttachNativeGetProp.
-    pshape.set(obj->lookupPure(id));
-
-    if (!pshape)
-        return false;
-
-    if (!pshape->hasSlot())
-        return false;
-
-    if (!pshape->hasDefaultSetter())
-        return false;
-
-    if (!pshape->writable())
-        return false;
-
-    *checkTypeset = false;
-    if (needsTypeBarrier && !CanInlineSetPropTypeCheck(obj, id, val, checkTypeset))
-        return false;
-
-    return true;
-}
-
-static bool
-PrototypeChainShadowsPropertyAdd(JSContext* cx, JSObject* obj, jsid id)
-{
-    // Walk up the object prototype chain and ensure that all prototypes
-    // are native, and that all prototypes have no getter or setter
-    // defined on the property
-    for (JSObject* proto = obj->staticPrototype(); proto; proto = proto->staticPrototype()) {
-        // If prototype is non-native, don't optimize
-        if (!proto->isNative())
-            return true;
-
-        // If prototype defines this property in a non-plain way, don't optimize
-        Shape* protoShape = proto->as<NativeObject>().lookupPure(id);
-        if (protoShape && !protoShape->hasDefaultSetter())
-            return true;
-
-        // Otherwise, if there's no such property, watch out for a resolve
-        // hook that would need to be invoked and thus prevent inlining of
-        // property addition.
-        if (ClassMayResolveId(cx->names(), proto->getClass(), id, proto))
-             return true;
-    }
-
-    return false;
-}
-
-static bool
-IsPropertyAddInlineable(JSContext* cx, NativeObject* obj, HandleId id,
-                        const ConstantOrRegister& val,
-                        HandleShape oldShape, bool needsTypeBarrier, bool* checkTypeset)
-{
-    // If the shape of the object did not change, then this was not an add.
-    if (obj->lastProperty() == oldShape)
-        return false;
-
-    Shape* shape = obj->lookupPure(id);
-    if (!shape || shape->inDictionary() || !shape->hasSlot() || !shape->hasDefaultSetter())
-        return false;
-
-    // If we have a shape at this point and the object's shape changed, then
-    // the shape must be the one we just added.
-    MOZ_ASSERT(shape == obj->lastProperty());
-
-    // Watch out for resolve hooks.
-    if (ClassMayResolveId(cx->names(), obj->getClass(), id, obj))
-        return false;
-
-    // Likewise for an addProperty hook, since we'll need to invoke it.
-    if (obj->getClass()->getAddProperty())
-        return false;
-
-    if (!obj->nonProxyIsExtensible() || !shape->writable())
-        return false;
-
-    if (PrototypeChainShadowsPropertyAdd(cx, obj, id))
-        return false;
-
-    // Don't attach if we are adding a property to an object which the new
-    // script properties analysis hasn't been performed for yet, as there
-    // may be a group change required here afterwards.
-    if (obj->group()->newScript() && !obj->group()->newScript()->analyzed())
-        return false;
-
-    *checkTypeset = false;
-    if (needsTypeBarrier && !CanInlineSetPropTypeCheck(obj, id, val, checkTypeset))
-        return false;
-
-    return true;
-}
-
-static SetPropertyIC::NativeSetPropCacheability
-CanAttachNativeSetProp(JSContext* cx, HandleObject obj, HandleId id, const ConstantOrRegister& val,
-                       bool needsTypeBarrier, MutableHandleObject holder,
-                       MutableHandleShape shape, bool* checkTypeset)
-{
-    // See if the property exists on the object.
-    if (obj->isNative() && IsPropertySetInlineable(&obj->as<NativeObject>(), id, shape, val,
-                                                   needsTypeBarrier, checkTypeset))
-    {
-        return SetPropertyIC::CanAttachSetSlot;
-    }
-
-    // If we couldn't find the property on the object itself, do a full, but
-    // still pure lookup for setters.
-    Rooted<PropertyResult> prop(cx);
-    if (!LookupPropertyPure(cx, obj, id, holder.address(), prop.address()))
-        return SetPropertyIC::CanAttachNone;
-
-    // If the object doesn't have the property, we don't know if we can attach
-    // a stub to add the property until we do the VM call to add. If the
-    // property exists as a data property on the prototype, we should add
-    // a new, shadowing property.
-    if (obj->isNative() &&
-        (!prop || (obj != holder && holder->isNative() &&
-                   prop.shape()->hasDefaultSetter() && prop.shape()->hasSlot())))
-    {
-        shape.set(prop.maybeShape());
-        return SetPropertyIC::MaybeCanAttachAddSlot;
-    }
-
-    if (prop.isNonNativeProperty())
-        return SetPropertyIC::CanAttachNone;
-
-    shape.set(prop.maybeShape());
-    if (IsCacheableSetPropCallPropertyOp(obj, holder, shape) ||
-        IsCacheableSetPropCallNative(obj, holder, shape) ||
-        IsCacheableSetPropCallScripted(obj, holder, shape))
-    {
-        return SetPropertyIC::CanAttachCallSetter;
-    }
-
-    return SetPropertyIC::CanAttachNone;
-}
-
-static void
-GenerateSetUnboxed(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& attacher,
-                   JSObject* obj, jsid id, uint32_t unboxedOffset, JSValueType unboxedType,
-                   Register object, Register tempReg, const ConstantOrRegister& value,
-                   bool checkTypeset, Label* failures)
-{
-    // Guard on the type of the object.
-    masm.branchPtr(Assembler::NotEqual,
-                   Address(object, JSObject::offsetOfGroup()),
-                   ImmGCPtr(obj->group()), failures);
-
-    if (checkTypeset)
-        CheckTypeSetForWrite(masm, obj, id, tempReg, value, failures);
-
-    Address address(object, UnboxedPlainObject::offsetOfData() + unboxedOffset);
-
-    if (cx->zone()->needsIncrementalBarrier()) {
-        if (unboxedType == JSVAL_TYPE_OBJECT)
-            masm.callPreBarrier(address, MIRType::Object);
-        else if (unboxedType == JSVAL_TYPE_STRING)
-            masm.callPreBarrier(address, MIRType::String);
-        else
-            MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(unboxedType));
-    }
-
-    masm.storeUnboxedProperty(address, unboxedType, value, failures);
-
-    attacher.jumpRejoin(masm);
-
-    masm.bind(failures);
-    attacher.jumpNextStub(masm);
-}
-
-static bool
-CanAttachSetUnboxed(JSContext* cx, HandleObject obj, HandleId id, const ConstantOrRegister& val,
-                    bool needsTypeBarrier, bool* checkTypeset,
-                    uint32_t* unboxedOffset, JSValueType* unboxedType)
-{
-    if (!obj->is<UnboxedPlainObject>())
-        return false;
-
-    const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(id);
-    if (property) {
-        *checkTypeset = false;
-        if (needsTypeBarrier && !CanInlineSetPropTypeCheck(obj, id, val, checkTypeset))
-            return false;
-        *unboxedOffset = property->offset;
-        *unboxedType = property->type;
-        return true;
-    }
-
-    return false;
-}
-
-static bool
-CanAttachSetUnboxedExpando(JSContext* cx, HandleObject obj, HandleId id,
-                           const ConstantOrRegister& val,
-                           bool needsTypeBarrier, bool* checkTypeset, Shape** pshape)
-{
-    if (!obj->is<UnboxedPlainObject>())
-        return false;
-
-    Rooted<UnboxedExpandoObject*> expando(cx, obj->as<UnboxedPlainObject>().maybeExpando());
-    if (!expando)
-        return false;
-
-    Shape* shape = expando->lookupPure(id);
-    if (!shape || !shape->hasDefaultSetter() || !shape->hasSlot() || !shape->writable())
-        return false;
-
-    *checkTypeset = false;
-    if (needsTypeBarrier && !CanInlineSetPropTypeCheck(obj, id, val, checkTypeset))
-        return false;
-
-    *pshape = shape;
-    return true;
-}
-
-static bool
-CanAttachAddUnboxedExpando(JSContext* cx, HandleObject obj, HandleShape oldShape,
-                           HandleId id, const ConstantOrRegister& val,
-                           bool needsTypeBarrier, bool* checkTypeset)
-{
-    if (!obj->is<UnboxedPlainObject>())
-        return false;
-
-    Rooted<UnboxedExpandoObject*> expando(cx, obj->as<UnboxedPlainObject>().maybeExpando());
-    if (!expando || expando->inDictionaryMode())
-        return false;
-
-    Shape* newShape = expando->lastProperty();
-    if (newShape->isEmptyShape() || newShape->propid() != id || newShape->previous() != oldShape)
-        return false;
-
-    MOZ_ASSERT(newShape->hasDefaultSetter() && newShape->hasSlot() && newShape->writable());
-
-    if (PrototypeChainShadowsPropertyAdd(cx, obj, id))
-        return false;
-
-    *checkTypeset = false;
-    if (needsTypeBarrier && !CanInlineSetPropTypeCheck(obj, id, val, checkTypeset))
-        return false;
-
-    return true;
-}
-
-bool
-SetPropertyIC::tryAttachUnboxed(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                                HandleObject obj, HandleId id, bool* emitted)
-{
-    MOZ_ASSERT(!*emitted);
-
-    bool checkTypeset = false;
-    uint32_t unboxedOffset;
-    JSValueType unboxedType;
-    if (!CanAttachSetUnboxed(cx, obj, id, value(), needsTypeBarrier(), &checkTypeset,
-                             &unboxedOffset, &unboxedType))
-    {
-        return true;
-    }
-
-    *emitted = true;
-
-    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
-    StubAttacher attacher(*this);
-
-    Label failures;
-    emitIdGuard(masm, id, &failures);
-
-    GenerateSetUnboxed(cx, masm, attacher, obj, id, unboxedOffset, unboxedType,
-                       object(), temp(), value(), checkTypeset, &failures);
-    return linkAndAttachStub(cx, masm, attacher, ion, "set_unboxed",
-                             JS::TrackedOutcome::ICSetPropStub_SetUnboxed);
-}
-
-bool
-SetPropertyIC::tryAttachProxy(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                              HandleObject obj, HandleId id, bool* emitted)
-{
-    MOZ_ASSERT(!*emitted);
-
-    if (!obj->is<ProxyObject>())
-        return true;
-
-    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, 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, id, returnAddr))
-            return false;
-        *emitted = true;
-        return true;
-    }
-
-    if (hasGenericProxyStub())
-        return true;
-
-    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)
-{
-    MOZ_ASSERT(!*emitted);
-    MOZ_ASSERT(!*tryNativeAddSlot);
-
-    RootedShape shape(cx);
-    RootedObject holder(cx);
-    bool checkTypeset = false;
-    NativeSetPropCacheability canCache = CanAttachNativeSetProp(cx, obj, id, value(), needsTypeBarrier(),
-                                                                &holder, &shape, &checkTypeset);
-    switch (canCache) {
-      case CanAttachNone:
-        return true;
-
-      case CanAttachSetSlot: {
-        RootedNativeObject nobj(cx, &obj->as<NativeObject>());
-        if (!attachSetSlot(cx, outerScript, ion, nobj, shape, checkTypeset))
-            return false;
-        *emitted = true;
-        return true;
-      }
-
-      case CanAttachCallSetter: {
-        void* returnAddr = GetReturnAddressToIonCode(cx);
-        if (!attachCallSetter(cx, outerScript, ion, obj, holder, shape, returnAddr))
-            return false;
-        *emitted = true;
-        return true;
-      }
-
-      case MaybeCanAttachAddSlot:
-        *tryNativeAddSlot = true;
-        return true;
-    }
-
-    MOZ_CRASH("Unreachable");
-}
-
-bool
-SetPropertyIC::tryAttachUnboxedExpando(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                                       HandleObject obj, HandleId id, bool* emitted)
-{
-    MOZ_ASSERT(!*emitted);
-
-    RootedShape shape(cx);
-    bool checkTypeset = false;
-    if (!CanAttachSetUnboxedExpando(cx, obj, id, value(), needsTypeBarrier(),
-                                    &checkTypeset, shape.address()))
-    {
-        return true;
-    }
-
-    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, HandleValue idval, HandleValue value,
-                             MutableHandleId id, bool* emitted, bool* tryNativeAddSlot)
-{
-    MOZ_ASSERT(!*emitted);
-    MOZ_ASSERT(!*tryNativeAddSlot);
-
-    if (!canAttachStub() || obj->watched())
-        return true;
-
-    // Fail cache emission if the object is frozen
-    if (obj->is<NativeObject>() && obj->as<NativeObject>().getElementsHeader()->isFrozen())
-        return true;
-
-    bool nameOrSymbol;
-    if (!ValueToNameOrSymbolId(cx, idval, id, &nameOrSymbol))
-        return false;
-
-    if (nameOrSymbol) {
-        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;
-
-        if (!*emitted && !tryAttachUnboxedExpando(cx, outerScript, ion, obj, id, emitted))
-            return false;
-    }
-
-    if (idval.isInt32()) {
-        if (!*emitted && !tryAttachDenseElement(cx, outerScript, ion, obj, idval, emitted))
-            return false;
-        if (!*emitted &&
-            !tryAttachTypedArrayElement(cx, outerScript, ion, obj, idval, value, emitted))
-        {
-            return false;
-        }
-    }
-
-    return true;
-}
-
-bool
-SetPropertyIC::tryAttachAddSlot(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                                HandleObject obj, HandleId id, HandleObjectGroup oldGroup,
-                                HandleShape oldShape, bool tryNativeAddSlot, bool* emitted)
-{
-    MOZ_ASSERT(!*emitted);
-
-    if (!canAttachStub())
-        return true;
-
-    if (!JSID_IS_STRING(id) && !JSID_IS_SYMBOL(id))
-        return true;
-
-    // Fail cache emission if the object is frozen
-    if (obj->is<NativeObject>() && obj->as<NativeObject>().getElementsHeader()->isFrozen())
-        return true;
-
-    // A GC may have caused cache.value() to become stale as it is not traced.
-    // In this case the IonScript will have been invalidated, so check for that.
-    // Assert no further GC is possible past this point.
-    JS::AutoAssertNoGC nogc;
-    if (ion->invalidated())
-        return true;
-
-    // The property did not exist before, now we can try to inline the property add.
-    bool checkTypeset = false;
-    if (tryNativeAddSlot &&
-        IsPropertyAddInlineable(cx, &obj->as<NativeObject>(), id, value(), oldShape,
-                                needsTypeBarrier(), &checkTypeset))
-    {
-        if (!attachAddSlot(cx, outerScript, ion, obj, id, oldShape, oldGroup, checkTypeset))
-            return false;
-        *emitted = true;
-        return true;
-    }
-
-    checkTypeset = false;
-    if (CanAttachAddUnboxedExpando(cx, obj, oldShape, id, value(), needsTypeBarrier(),
-                                   &checkTypeset))
-    {
-        if (!attachAddSlot(cx, outerScript, ion, obj, id, oldShape, oldGroup, checkTypeset))
-            return false;
-        *emitted = true;
-        return true;
-    }
-
-    return true;
-}
-
-bool
-SetPropertyIC::update(JSContext* cx, HandleScript outerScript, size_t cacheIndex, HandleObject obj,
-                      HandleValue idval, HandleValue value)
-{
-    IonScript* ion = outerScript->ionScript();
-    SetPropertyIC& cache = ion->getCache(cacheIndex).toSetProperty();
-
-    // 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 = JSObject::getGroup(cx, obj);
-        if (!oldGroup)
-            return false;
-
-        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, idval, value, &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()->lexicalEnvironment(), script, pc, value);
-    } else if (*cache.pc() == JSOP_SETELEM || *cache.pc() == JSOP_STRICTSETELEM) {
-        if (!SetObjectElement(cx, obj, idval, value, cache.strict()))
-            return false;
-    } 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))
-    {
-        return false;
-    }
-
-    if (!emitted)
-        JitSpew(JitSpew_IonIC, "Failed to attach SETPROP cache");
-
-    return true;
-}
-
-void
-SetPropertyIC::reset(ReprotectCode reprotect)
-{
-    IonCache::reset(reprotect);
-    hasGenericProxyStub_ = false;
-    hasDenseStub_ = false;
-}
-
-static bool
-IsDenseElementSetInlineable(JSObject* obj, const Value& idval, const ConstantOrRegister& val,
-                            bool needsTypeBarrier, bool* checkTypeset)
-{
-    if (!obj->is<ArrayObject>())
-        return false;
-
-    if (obj->watched())
-        return false;
-
-    if (!idval.isInt32())
-        return false;
-
-    // The object may have a setter definition,
-    // either directly, or via a prototype, or via the target object for a prototype
-    // which is a proxy, that handles a particular integer write.
-    // Scan the prototype and shape chain to make sure that this is not the case.
-    JSObject* curObj = obj;
-    while (curObj) {
-        // Ensure object is native.  (This guarantees static prototype below.)
-        if (!curObj->isNative())
-            return false;
-
-        // Ensure all indexed properties are stored in dense elements.
-        if (curObj->isIndexed())
-            return false;
-
-        curObj = curObj->staticPrototype();
-    }
-
-    *checkTypeset = false;
-    if (needsTypeBarrier && !CanInlineSetPropTypeCheck(obj, JSID_VOID, val, checkTypeset))
-        return false;
-
-    return true;
-}
-
-static bool
-IsTypedArrayElementSetInlineable(JSObject* obj, const Value& idval, const Value& value)
-{
-    // Don't bother attaching stubs for assigning strings, objects or symbols.
-    return obj->is<TypedArrayObject>() && idval.isInt32() &&
-           !value.isString() && !value.isObject() && !value.isSymbol();
-}
-
-static void
-StoreDenseElement(MacroAssembler& masm, const ConstantOrRegister& value, Register elements,
-                  BaseObjectElementIndex target)
+jit::EmitIonStoreDenseElement(MacroAssembler& masm, const ConstantOrRegister& value,
+                              Register elements, BaseObjectElementIndex target)
 {
     // If the ObjectElements::CONVERT_DOUBLE_ELEMENTS flag is set, int32 values
     // have to be converted to double first. If the value is not int32, it can
     // always be stored directly.
 
     Address elementsFlags(elements, ObjectElements::offsetOfFlags());
     if (value.constant()) {
         Value v = value.value();
@@ -2758,268 +1169,16 @@ StoreDenseElement(MacroAssembler& masm, 
         MOZ_ASSERT(reg.type() == MIRType::Int32);
         masm.convertInt32ToDouble(reg.typedReg().gpr(), ScratchDoubleReg);
         masm.storeDouble(ScratchDoubleReg, target);
     }
 
     masm.bind(&done);
 }
 
-static bool
-GenerateSetDenseElement(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& attacher,
-                        JSObject* obj, const Value& idval, bool guardHoles, Register object,
-                        TypedOrValueRegister index, const ConstantOrRegister& value,
-                        Register tempToUnboxIndex, Register temp,
-                        bool needsTypeBarrier, bool checkTypeset)
-{
-    MOZ_ASSERT(obj->isNative());
-    MOZ_ASSERT(idval.isInt32());
-
-    Label failures;
-
-    // Guard object is a dense array.
-    Shape* shape = obj->as<NativeObject>().lastProperty();
-    if (!shape)
-        return false;
-    masm.branchTestObjShape(Assembler::NotEqual, object, shape, &failures);
-
-    // Guard that the incoming value is in the type set for the property
-    // if a type barrier is required.
-    if (needsTypeBarrier) {
-        masm.branchTestObjGroup(Assembler::NotEqual, object, obj->group(), &failures);
-        if (checkTypeset)
-            CheckTypeSetForWrite(masm, obj, JSID_VOID, temp, value, &failures);
-    }
-
-    // Ensure the index is an int32 value.
-    Register indexReg;
-    if (index.hasValue()) {
-        ValueOperand val = index.valueReg();
-        masm.branchTestInt32(Assembler::NotEqual, val, &failures);
-
-        indexReg = masm.extractInt32(val, tempToUnboxIndex);
-    } else {
-        MOZ_ASSERT(!index.typedReg().isFloat());
-        indexReg = index.typedReg().gpr();
-    }
-
-    {
-        // Load obj->elements.
-        Register elements = temp;
-        masm.loadPtr(Address(object, NativeObject::offsetOfElements()), elements);
-
-        // Compute the location of the element.
-        BaseObjectElementIndex target(elements, indexReg);
-
-        Label storeElement;
-
-        // If TI cannot help us deal with HOLES by preventing indexed properties
-        // on the prototype chain, we have to be very careful to check for ourselves
-        // to avoid stomping on what should be a setter call. Start by only allowing things
-        // within the initialized length.
-        if (guardHoles) {
-            Address initLength(elements, ObjectElements::offsetOfInitializedLength());
-            masm.branch32(Assembler::BelowOrEqual, initLength, indexReg, &failures);
-        } else {
-            // Guard that we can increase the initialized length.
-            Address capacity(elements, ObjectElements::offsetOfCapacity());
-            masm.branch32(Assembler::BelowOrEqual, capacity, indexReg, &failures);
-
-            // Guard on the initialized length.
-            Address initLength(elements, ObjectElements::offsetOfInitializedLength());
-            masm.branch32(Assembler::Below, initLength, indexReg, &failures);
-
-            // if (initLength == index)
-            Label inBounds;
-            masm.branch32(Assembler::NotEqual, initLength, indexReg, &inBounds);
-            {
-                // Increase initialize length.
-                Register newLength = indexReg;
-                masm.add32(Imm32(1), newLength);
-                masm.store32(newLength, initLength);
-
-                // Increase length if needed.
-                Label bumpedLength;
-                Address length(elements, ObjectElements::offsetOfLength());
-                masm.branch32(Assembler::AboveOrEqual, length, indexReg, &bumpedLength);
-                masm.store32(newLength, length);
-                masm.bind(&bumpedLength);
-
-                // Restore the index.
-                masm.add32(Imm32(-1), newLength);
-                masm.jump(&storeElement);
-            }
-            // else
-            masm.bind(&inBounds);
-        }
-
-        if (cx->zone()->needsIncrementalBarrier())
-            masm.callPreBarrier(target, MIRType::Value);
-
-        // Store the value.
-        if (guardHoles)
-            masm.branchTestMagic(Assembler::Equal, target, &failures);
-        else
-            masm.bind(&storeElement);
-        StoreDenseElement(masm, value, elements, target);
-    }
-    attacher.jumpRejoin(masm);
-
-    masm.bind(&failures);
-    attacher.jumpNextStub(masm);
-
-    return true;
-}
-
-bool
-SetPropertyIC::tryAttachDenseElement(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                                     HandleObject obj, const Value& idval, bool* emitted)
-{
-    MOZ_ASSERT(!*emitted);
-    MOZ_ASSERT(canAttachStub());
-
-    if (hasDenseStub())
-        return true;
-
-    bool checkTypeset = false;
-    if (!IsDenseElementSetInlineable(obj, idval, value(), needsTypeBarrier(), &checkTypeset))
-        return true;
-
-    *emitted = true;
-
-    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
-    StubAttacher attacher(*this);
-    if (!GenerateSetDenseElement(cx, masm, attacher, obj, idval,
-                                 guardHoles(), object(), id().reg(),
-                                 value(), tempToUnboxIndex(), temp(),
-                                 needsTypeBarrier(), checkTypeset))
-    {
-        return false;
-    }
-
-    setHasDenseStub();
-    const char* message = guardHoles() ?  "dense array (holes)" : "dense array";
-    return linkAndAttachStub(cx, masm, attacher, ion, message,
-                             JS::TrackedOutcome::ICSetElemStub_Dense);
-}
-
-static bool
-GenerateSetTypedArrayElement(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& attacher,
-                             HandleObject tarr, Register object, TypedOrValueRegister index,
-                             const ConstantOrRegister& value, Register tempUnbox, Register temp,
-                             FloatRegister tempDouble, FloatRegister tempFloat32)
-{
-    Label failures, done, popObjectAndFail;
-
-    // Guard on the shape.
-    Shape* shape = tarr->as<TypedArrayObject>().lastProperty();
-    if (!shape)
-        return false;
-    masm.branchTestObjShape(Assembler::NotEqual, object, shape, &failures);
-
-    // Ensure the index is an int32.
-    Register indexReg;
-    if (index.hasValue()) {
-        ValueOperand val = index.valueReg();
-        masm.branchTestInt32(Assembler::NotEqual, val, &failures);
-
-        indexReg = masm.extractInt32(val, tempUnbox);
-    } else {
-        MOZ_ASSERT(!index.typedReg().isFloat());
-        indexReg = index.typedReg().gpr();
-    }
-
-    // Guard on the length.
-    Address length(object, TypedArrayObject::lengthOffset());
-    masm.unboxInt32(length, temp);
-    masm.branch32(Assembler::BelowOrEqual, temp, indexReg, &done);
-
-    // Load the elements vector.
-    Register elements = temp;
-    masm.loadPtr(Address(object, TypedArrayObject::dataOffset()), elements);
-
-    // Set the value.
-    Scalar::Type arrayType = tarr->as<TypedArrayObject>().type();
-    int width = Scalar::byteSize(arrayType);
-    BaseIndex target(elements, indexReg, ScaleFromElemWidth(width));
-
-    if (arrayType == Scalar::Float32) {
-        MOZ_ASSERT_IF(hasUnaliasedDouble(), tempFloat32 != InvalidFloatReg);
-        FloatRegister tempFloat = hasUnaliasedDouble() ? tempFloat32 : tempDouble;
-        if (!masm.convertConstantOrRegisterToFloat(cx, value, tempFloat, &failures))
-            return false;
-        masm.storeToTypedFloatArray(arrayType, tempFloat, target);
-    } else if (arrayType == Scalar::Float64) {
-        if (!masm.convertConstantOrRegisterToDouble(cx, value, tempDouble, &failures))
-            return false;
-        masm.storeToTypedFloatArray(arrayType, tempDouble, target);
-    } else {
-        // On x86 we only have 6 registers available to use, so reuse the object
-        // register to compute the intermediate value to store and restore it
-        // afterwards.
-        masm.push(object);
-
-        if (arrayType == Scalar::Uint8Clamped) {
-            if (!masm.clampConstantOrRegisterToUint8(cx, value, tempDouble, object,
-                                                     &popObjectAndFail))
-            {
-                return false;
-            }
-        } else {
-            if (!masm.truncateConstantOrRegisterToInt32(cx, value, tempDouble, object,
-                                                        &popObjectAndFail))
-            {
-                return false;
-            }
-        }
-        masm.storeToTypedIntArray(arrayType, object, target);
-
-        masm.pop(object);
-    }
-
-    // Out-of-bound writes jump here as they are no-ops.
-    masm.bind(&done);
-    attacher.jumpRejoin(masm);
-
-    if (popObjectAndFail.used()) {
-        masm.bind(&popObjectAndFail);
-        masm.pop(object);
-    }
-
-    masm.bind(&failures);
-    attacher.jumpNextStub(masm);
-    return true;
-}
-
-bool
-SetPropertyIC::tryAttachTypedArrayElement(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                                          HandleObject obj, HandleValue idval, HandleValue val,
-                                          bool* emitted)
-{
-    MOZ_ASSERT(!*emitted);
-    MOZ_ASSERT(canAttachStub());
-
-    if (!IsTypedArrayElementSetInlineable(obj, idval, val))
-        return true;
-
-    *emitted = true;
-
-    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
-    StubAttacher attacher(*this);
-    if (!GenerateSetTypedArrayElement(cx, masm, attacher, obj,
-                                      object(), id().reg(), value(),
-                                      tempToUnboxIndex(), temp(), tempDouble(), tempFloat32()))
-    {
-        return false;
-    }
-
-    return linkAndAttachStub(cx, masm, attacher, ion, "typed array",
-                             JS::TrackedOutcome::ICSetElemStub_TypedArray);
-}
-
 bool
 BindNameIC::attachGlobal(JSContext* cx, HandleScript outerScript, IonScript* ion,
                          HandleObject envChain)
 {
     MOZ_ASSERT(envChain->is<GlobalObject>());
 
     MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     StubAttacher attacher(*this);
--- a/js/src/jit/IonCaches.h
+++ b/js/src/jit/IonCaches.h
@@ -24,17 +24,16 @@
 #include "vm/TypedArrayObject.h"
 
 namespace js {
 namespace jit {
 
 class LInstruction;
 
 #define IONCACHE_KIND_LIST(_)                                   \
-    _(SetProperty)                                              \
     _(BindName)                                                 \
     _(Name)
 
 // Forward declarations of Cache kinds.
 #define FORWARD_DECLARE(kind) class kind##IC;
 IONCACHE_KIND_LIST(FORWARD_DECLARE)
 #undef FORWARD_DECLARE
 
@@ -355,168 +354,16 @@ class IonCache
         visitor->visit##ickind##IC(codegen);                        \
     }                                                               \
                                                                     \
     static const VMFunction UpdateInfo;
 
 // Subclasses of IonCache for the various kinds of caches. These do not define
 // new data members; all caches must be of the same size.
 
-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_;
-    Register tempToUnboxIndex_;
-    FloatRegister tempDouble_;
-    FloatRegister tempFloat32_;
-    ConstantOrRegister id_;
-    ConstantOrRegister value_;
-    bool strict_ : 1;
-    bool needsTypeBarrier_ : 1;
-    bool guardHoles_ : 1;
-
-    bool hasGenericProxyStub_ : 1;
-    bool hasDenseStub_ : 1;
-
-    void emitIdGuard(MacroAssembler& masm, jsid id, Label* fail);
-
-  public:
-    SetPropertyIC(LiveRegisterSet liveRegs, Register object, Register temp, Register tempToUnboxIndex,
-                  FloatRegister tempDouble, FloatRegister tempFloat32,
-                  const ConstantOrRegister& id, const ConstantOrRegister& value,
-                  bool strict, bool needsTypeBarrier, bool guardHoles)
-      : liveRegs_(liveRegs),
-        object_(object),
-        temp_(temp),
-        tempToUnboxIndex_(tempToUnboxIndex),
-        tempDouble_(tempDouble),
-        tempFloat32_(tempFloat32),
-        id_(id),
-        value_(value),
-        strict_(strict),
-        needsTypeBarrier_(needsTypeBarrier),
-        guardHoles_(guardHoles),
-        hasGenericProxyStub_(false),
-        hasDenseStub_(false)
-    {
-    }
-
-    CACHE_HEADER(SetProperty)
-
-    void reset(ReprotectCode reprotect);
-
-    Register object() const {
-        return object_;
-    }
-    Register temp() const {
-        return temp_;
-    }
-    Register tempToUnboxIndex() const {
-        return tempToUnboxIndex_;
-    }
-    FloatRegister tempDouble() const {
-        return tempDouble_;
-    }
-    FloatRegister tempFloat32() const {
-        return tempFloat32_;
-    }
-    ConstantOrRegister id() const {
-        return id_;
-    }
-    ConstantOrRegister value() const {
-        return value_;
-    }
-    bool strict() const {
-        return strict_;
-    }
-    bool needsTypeBarrier() const {
-        return needsTypeBarrier_;
-    }
-    bool guardHoles() const {
-        return guardHoles_;
-    }
-    bool hasGenericProxyStub() const {
-        return hasGenericProxyStub_;
-    }
-
-    bool hasDenseStub() const {
-        return hasDenseStub_;
-    }
-    void setHasDenseStub() {
-        MOZ_ASSERT(!hasDenseStub());
-        hasDenseStub_ = true;
-    }
-
-    enum NativeSetPropCacheability {
-        CanAttachNone,
-        CanAttachSetSlot,
-        MaybeCanAttachAddSlot,
-        CanAttachCallSetter
-    };
-
-    MOZ_MUST_USE bool attachSetSlot(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                                    HandleObject obj, HandleShape shape, bool checkTypeset);
-
-    MOZ_MUST_USE bool attachCallSetter(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                                       HandleObject obj, HandleObject holder, HandleShape shape,
-                                       void* returnAddr);
-
-    MOZ_MUST_USE bool attachAddSlot(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                                    HandleObject obj, HandleId id, HandleShape oldShape,
-                                    HandleObjectGroup oldGroup, bool checkTypeset);
-
-    MOZ_MUST_USE bool attachGenericProxy(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                                         HandleId id, void* returnAddr);
-
-    MOZ_MUST_USE bool attachDOMProxyShadowed(JSContext* cx, HandleScript outerScript,
-                                             IonScript* ion, HandleObject obj, HandleId id,
-                                             void* returnAddr);
-
-    MOZ_MUST_USE bool attachDOMProxyUnshadowed(JSContext* cx, HandleScript outerScript,
-                                               IonScript* ion, HandleObject obj, HandleId id,
-                                               void* returnAddr);
-
-    static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript, size_t cacheIndex,
-                                    HandleObject obj, HandleValue idval, HandleValue value);
-
-    MOZ_MUST_USE bool tryAttachNative(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                                      HandleObject obj, HandleId id, bool* emitted,
-                                      bool* tryNativeAddSlot);
-
-    MOZ_MUST_USE bool tryAttachUnboxed(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                                       HandleObject obj, HandleId id, bool* emitted);
-
-    MOZ_MUST_USE bool tryAttachUnboxedExpando(JSContext* cx, HandleScript outerScript,
-                                              IonScript* ion, HandleObject obj, HandleId id,
-                                              bool* emitted);
-
-    MOZ_MUST_USE bool tryAttachProxy(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                                     HandleObject obj, HandleId id, bool* emitted);
-
-    MOZ_MUST_USE bool tryAttachStub(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                                    HandleObject obj, HandleValue idval, HandleValue value,
-                                    MutableHandleId id, bool* emitted, bool* tryNativeAddSlot);
-
-    MOZ_MUST_USE bool tryAttachAddSlot(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                                       HandleObject obj, HandleId id, HandleObjectGroup oldGroup,
-                                       HandleShape oldShape, bool tryNativeAddSlot, bool* emitted);
-
-    MOZ_MUST_USE bool tryAttachDenseElement(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                                            HandleObject obj, const Value& idval, bool* emitted);
-
-    MOZ_MUST_USE bool tryAttachTypedArrayElement(JSContext* cx, HandleScript outerScript,
-                                                 IonScript* ion, HandleObject obj,
-                                                 HandleValue idval, HandleValue val, bool* emitted);
-};
-
 class BindNameIC : public IonCache
 {
   protected:
     Register environmentChain_;
     PropertyName* name_;
     Register output_;
 
   public:
@@ -634,12 +481,15 @@ bool IsCacheableSetPropCallScripted(JSOb
                                     bool* isTemporarilyUnoptimizable = nullptr);
 bool IsCacheableSetPropCallNative(JSObject* obj, JSObject* holder, Shape* shape);
 
 bool ValueToNameOrSymbolId(JSContext* cx, HandleValue idval, MutableHandleId id,
                            bool* nameOrSymbol);
 
 void* GetReturnAddressToIonCode(JSContext* cx);
 
+void EmitIonStoreDenseElement(MacroAssembler& masm, const ConstantOrRegister& value,
+                              Register elements, BaseObjectElementIndex target);
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_IonCaches_h */
--- a/js/src/jit/IonIC.cpp
+++ b/js/src/jit/IonIC.cpp
@@ -36,19 +36,20 @@ IonIC::scratchRegisterForEntryJump()
       case CacheKind::GetProp:
       case CacheKind::GetElem: {
         Register temp = asGetPropertyIC()->maybeTemp();
         if (temp != InvalidReg)
             return temp;
         TypedOrValueRegister output = asGetPropertyIC()->output();
         return output.hasValue() ? output.valueReg().scratchReg() : output.typedReg().gpr();
       }
-      case CacheKind::GetName:
       case CacheKind::SetProp:
       case CacheKind::SetElem:
+        return asSetPropertyIC()->temp1();
+      case CacheKind::GetName:
       case CacheKind::In:
         MOZ_CRASH("Baseline-specific for now");
     }
 
     MOZ_CRASH("Invalid kind");
 }
 
 void
@@ -89,16 +90,29 @@ IonIC::trace(JSTracer* trc)
 
         nextCodeRaw = stub->nextCodeRaw();
     }
 
     MOZ_ASSERT(nextCodeRaw == fallbackLabel_.raw());
 }
 
 void
+IonIC::togglePreBarriers(bool enabled, ReprotectCode reprotect)
+{
+    uint8_t* nextCodeRaw = codeRaw_;
+    for (IonICStub* stub = firstStub_; stub; stub = stub->next()) {
+        JitCode* code = JitCode::FromExecutable(nextCodeRaw);
+        code->togglePreBarriers(enabled, reprotect);
+        nextCodeRaw = stub->nextCodeRaw();
+    }
+
+    MOZ_ASSERT(nextCodeRaw == fallbackLabel_.raw());
+}
+
+void
 IonGetPropertyIC::maybeDisable(Zone* zone, bool attached)
 {
     if (attached) {
         failedUpdates_ = 0;
         return;
     }
 
     if (!canAttachStub() && kind() == CacheKind::GetProp) {
@@ -116,17 +130,18 @@ IonGetPropertyIC::maybeDisable(Zone* zon
     }
 }
 
 /* static */ bool
 IonGetPropertyIC::update(JSContext* cx, HandleScript outerScript, IonGetPropertyIC* ic,
 			 HandleValue val, HandleValue idVal, MutableHandleValue res)
 {
     // Override the return value if we are invalidated (bug 728188).
-    AutoDetectInvalidation adi(cx, res, outerScript->ionScript());
+    IonScript* ionScript = outerScript->ionScript();
+    AutoDetectInvalidation adi(cx, res, ionScript);
 
     // If the IC is idempotent, we will redo the op in the interpreter.
     if (ic->idempotent())
         adi.disable();
 
     bool attached = false;
     if (!JitOptions.disableCacheIR && !ic->disabled()) {
         if (ic->canAttachStub()) {
@@ -136,18 +151,17 @@ IonGetPropertyIC::update(JSContext* cx, 
             // stub if we inserted a type barrier.
             CanAttachGetter canAttachGetter =
                 ic->monitoredResult() ? CanAttachGetter::Yes : CanAttachGetter::No;
             jsbytecode* pc = ic->idempotent() ? nullptr : ic->pc();
             bool isTemporarilyUnoptimizable;
             GetPropIRGenerator gen(cx, outerScript, pc, ic->kind(), &isTemporarilyUnoptimizable,
                                    val, idVal, canAttachGetter);
             if (ic->idempotent() ? gen.tryAttachIdempotentStub() : gen.tryAttachStub()) {
-                attached = ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
-                                                 outerScript);
+                attached = ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript);
             }
         }
         ic->maybeDisable(cx->zone(), attached);
     }
 
     if (!attached && ic->idempotent()) {
         // Invalidate the cache if the property was not found, or was found on
         // a non-native object. This ensures:
@@ -182,16 +196,84 @@ IonGetPropertyIC::update(JSContext* cx, 
         // Monitor changes to cache entry.
         if (!ic->monitoredResult())
             TypeScript::Monitor(cx, ic->script(), ic->pc(), res);
     }
 
     return true;
 }
 
+/* static */ bool
+IonSetPropertyIC::update(JSContext* cx, HandleScript outerScript, IonSetPropertyIC* ic,
+			 HandleObject obj, HandleValue idVal, HandleValue rhs)
+{
+    RootedShape oldShape(cx);
+    RootedObjectGroup oldGroup(cx);
+    IonScript* ionScript = outerScript->ionScript();
+
+    bool attached = false;
+    if (!JitOptions.disableCacheIR && ic->canAttachStub()) {
+        oldShape = obj->maybeShape();
+        oldGroup = JSObject::getGroup(cx, obj);
+        if (!oldGroup)
+            return false;
+        if (obj->is<UnboxedPlainObject>()) {
+            MOZ_ASSERT(!oldShape);
+            if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando())
+                oldShape = expando->lastProperty();
+        }
+
+        RootedValue objv(cx, ObjectValue(*obj));
+        RootedScript script(cx, ic->script());
+        jsbytecode* pc = ic->pc();
+        bool isTemporarilyUnoptimizable;
+        SetPropIRGenerator gen(cx, script, pc, ic->kind(), &isTemporarilyUnoptimizable,
+                               objv, idVal, rhs, ic->needsTypeBarrier(), ic->guardHoles());
+        if (gen.tryAttachStub()) {
+            attached = ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
+                                             ionScript, gen.typeCheckInfo());
+        }
+    }
+
+    if (ic->kind() == CacheKind::SetElem) {
+        if (!SetObjectElement(cx, obj, idVal, rhs, ic->strict()))
+            return false;
+    } else {
+        MOZ_ASSERT(ic->kind() == CacheKind::SetProp);
+
+        jsbytecode* pc = ic->pc();
+        if (*pc == JSOP_INITGLEXICAL) {
+            RootedScript script(cx, ic->script());
+            MOZ_ASSERT(!script->hasNonSyntacticScope());
+            InitGlobalLexicalOperation(cx, &cx->global()->lexicalEnvironment(), script, pc, rhs);
+        } else {
+            RootedPropertyName name(cx, idVal.toString()->asAtom().asPropertyName());
+            if (!SetProperty(cx, obj, name, rhs, ic->strict(), pc))
+                return false;
+        }
+    }
+
+    if (!attached && !JitOptions.disableCacheIR && ic->canAttachStub()) {
+        RootedValue objv(cx, ObjectValue(*obj));
+        RootedScript script(cx, ic->script());
+        jsbytecode* pc = ic->pc();
+        bool isTemporarilyUnoptimizable;
+        SetPropIRGenerator gen(cx, script, pc, ic->kind(), &isTemporarilyUnoptimizable,
+                               objv, idVal, rhs, ic->needsTypeBarrier(), ic->guardHoles());
+        if (gen.tryAttachAddSlotStub(oldGroup, oldShape)) {
+            attached = ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
+                                             ionScript, gen.typeCheckInfo());
+        } else {
+            gen.trackNotAttached();
+        }
+    }
+
+    return true;
+}
+
 uint8_t*
 IonICStub::stubDataStart()
 {
     return reinterpret_cast<uint8_t*>(this) + stubInfo_->stubDataOffset();
 }
 
 void
 IonIC::attachStub(IonICStub* newStub, JitCode* code)
--- a/js/src/jit/IonIC.h
+++ b/js/src/jit/IonIC.h
@@ -52,16 +52,17 @@ class IonICStub
     void poison() {
         nextCodeRaw_ = nullptr;
         next_ = nullptr;
         stubInfo_ = nullptr;
     }
 };
 
 class IonGetPropertyIC;
+class IonSetPropertyIC;
 
 class IonIC
 {
     // This either points at the OOL path for the fallback path, or the code for
     // the first stub.
     uint8_t* codeRaw_;
 
     // The first optimized stub, or nullptr.
@@ -120,40 +121,47 @@ class IonIC
         disabled_ = true;
     }
 
     bool disabled() const { return disabled_; }
 
     // Discard all stubs.
     void reset(Zone* zone);
 
+    void togglePreBarriers(bool enabled, ReprotectCode reprotect);
+
     CacheKind kind() const { return kind_; }
     uint8_t** codeRawPtr() { return &codeRaw_; }
 
     bool idempotent() const { return idempotent_; }
     void setIdempotent() { idempotent_ = true; }
 
     void setFallbackLabel(CodeOffset fallbackLabel) { fallbackLabel_ = fallbackLabel; }
     void setRejoinLabel(CodeOffset rejoinLabel) { rejoinLabel_ = rejoinLabel; }
 
     IonGetPropertyIC* asGetPropertyIC() {
         MOZ_ASSERT(kind_ == CacheKind::GetProp || kind_ == CacheKind::GetElem);
         return (IonGetPropertyIC*)this;
     }
+    IonSetPropertyIC* asSetPropertyIC() {
+        MOZ_ASSERT(kind_ == CacheKind::SetProp || kind_ == CacheKind::SetElem);
+        return (IonSetPropertyIC*)this;
+    }
 
     void updateBaseAddress(JitCode* code, MacroAssembler& masm);
 
     // Returns the Register to use as scratch when entering IC stubs. This
     // should either be an output register or a temp.
     Register scratchRegisterForEntryJump();
 
     void trace(JSTracer* trc);
 
     bool attachCacheIRStub(JSContext* cx, const CacheIRWriter& writer, CacheKind kind,
-                           HandleScript outerScript);
+                           IonScript* ionScript,
+                           const PropertyTypeCheckInfo* typeCheckInfo = nullptr);
 };
 
 class IonGetPropertyIC : public IonIC
 {
     LiveRegisterSet liveRegs_;
 
     TypedOrValueRegister value_;
     ConstantOrRegister id_;
@@ -190,12 +198,61 @@ class IonGetPropertyIC : public IonIC
     bool allowDoubleResult() const { return allowDoubleResult_; }
 
     void maybeDisable(Zone* zone, bool attached);
 
     static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript, IonGetPropertyIC* ic,
                                     HandleValue val, HandleValue idVal, MutableHandleValue res);
 };
 
+class IonSetPropertyIC : public IonIC
+{
+    LiveRegisterSet liveRegs_;
+
+    Register object_;
+    Register temp1_;
+    FloatRegister maybeTempDouble_;
+    FloatRegister maybeTempFloat32_;
+    ConstantOrRegister id_;
+    ConstantOrRegister rhs_;
+    bool strict_ : 1;
+    bool needsTypeBarrier_ : 1;
+    bool guardHoles_ : 1;
+
+  public:
+    IonSetPropertyIC(CacheKind kind, LiveRegisterSet liveRegs, Register object, Register temp1,
+                     FloatRegister maybeTempDouble, FloatRegister maybeTempFloat32,
+                     const ConstantOrRegister& id, const ConstantOrRegister& rhs, bool strict,
+                     bool needsTypeBarrier, bool guardHoles)
+      : IonIC(kind),
+        liveRegs_(liveRegs),
+        object_(object),
+        temp1_(temp1),
+        maybeTempDouble_(maybeTempDouble),
+        maybeTempFloat32_(maybeTempFloat32),
+        id_(id),
+        rhs_(rhs),
+        strict_(strict),
+        needsTypeBarrier_(needsTypeBarrier),
+        guardHoles_(guardHoles)
+    { }
+
+    LiveRegisterSet liveRegs() const { return liveRegs_; }
+    Register object() const { return object_; }
+    ConstantOrRegister id() const { return id_; }
+    ConstantOrRegister rhs() const { return rhs_; }
+
+    Register temp1() const { return temp1_; }
+    FloatRegister maybeTempDouble() const { return maybeTempDouble_; }
+    FloatRegister maybeTempFloat32() const { return maybeTempFloat32_; }
+
+    bool strict() const { return strict_; }
+    bool needsTypeBarrier() const { return needsTypeBarrier_; }
+    bool guardHoles() const { return guardHoles_; }
+
+    static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript, IonSetPropertyIC* ic,
+                                    HandleObject obj, HandleValue idVal, HandleValue rhs);
+};
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_IonIC_h */
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -3970,35 +3970,30 @@ LIRGenerator::visitSetPropertyCache(MSet
     bool useConstId = id->type() == MIRType::String || id->type() == MIRType::Symbol;
     bool useConstValue = IsNonNurseryConstant(ins->value());
 
     // 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();
 
-    // If the index might be an integer, we need some extra temp registers for
-    // the dense and typed array element stubs.
-    LDefinition tempToUnboxIndex = LDefinition::BogusTemp();
+    // We need a double/float32 temp register for typed array stubs if this is
+    // a SETELEM.
     LDefinition tempD = LDefinition::BogusTemp();
     LDefinition tempF32 = LDefinition::BogusTemp();
-
-    if (id->mightBeType(MIRType::Int32)) {
-        if (id->type() != MIRType::Int32)
-            tempToUnboxIndex = tempToUnbox();
+    if (IsSetElemPC(ins->resumePoint()->pc())) {
         tempD = tempDouble();
         tempF32 = hasUnaliasedDouble() ? tempFloat32() : LDefinition::BogusTemp();
     }
 
     LInstruction* lir =
         new(alloc()) LSetPropertyCache(useRegister(ins->object()),
                                        useBoxOrTypedOrConstant(id, useConstId),
                                        useBoxOrTypedOrConstant(ins->value(), useConstValue),
-                                       temp(),
-                                       tempToUnboxIndex, tempD, tempF32);
+                                       temp(), tempD, tempF32);
     add(lir, ins);
     assignSafepoint(lir, ins);
 }
 
 void
 LIRGenerator::visitCallSetElement(MCallSetElement* ins)
 {
     MOZ_ASSERT(ins->object()->type() == MIRType::Object);
--- a/js/src/jit/RegisterSets.h
+++ b/js/src/jit/RegisterSets.h
@@ -1026,16 +1026,29 @@ class CommonRegSet : public SpecializedR
         add(value.payloadReg());
         add(value.typeReg());
 #elif defined(JS_PUNBOX64)
         add(value.valueReg());
 #else
 #error "Bad architecture"
 #endif
     }
+
+    using Parent::addUnchecked;
+    void addUnchecked(ValueOperand value) {
+#if defined(JS_NUNBOX32)
+        addUnchecked(value.payloadReg());
+        addUnchecked(value.typeReg());
+#elif defined(JS_PUNBOX64)
+        addUnchecked(value.valueReg());
+#else
+#error "Bad architecture"
+#endif
+    }
+
     void add(TypedOrValueRegister reg) {
         if (reg.hasValue())
             add(reg.valueReg());
         else if (reg.hasTyped())
             add(reg.typedReg());
     }
 
     using Parent::take;
--- a/js/src/jit/arm/Architecture-arm.h
+++ b/js/src/jit/arm/Architecture-arm.h
@@ -409,19 +409,20 @@ class VFPRegister
     constexpr VFPRegister(RegType k, uint32_t id, bool invalid, bool missing) :
         kind(k), code_(Code(id)), _isInvalid(invalid), _isMissing(missing) {
     }
 
     explicit constexpr VFPRegister(Code id)
       : kind(Double), code_(id), _isInvalid(false), _isMissing(false)
     { }
     bool operator==(const VFPRegister& other) const {
-        MOZ_ASSERT(!isInvalid());
-        MOZ_ASSERT(!other.isInvalid());
-        return kind == other.kind && code_ == other.code_;
+        return kind == other.kind && code_ == other.code_ && isInvalid() == other.isInvalid();
+    }
+    bool operator!=(const VFPRegister& other) const {
+        return !operator==(other);
     }
 
     bool isSingle() const { return kind == Single; }
     bool isDouble() const { return kind == Double; }
     bool isSimd128() const { return false; }
     bool isFloat() const { return (kind == Double) || (kind == Single); }
     bool isInt() const { return (kind == UInt) || (kind == Int); }
     bool isSInt() const { return kind == Int; }
@@ -483,19 +484,16 @@ class VFPRegister
             return !!((1 << (code_ >> 1)) & FloatRegisters::VolatileMask);
         return !!((1 << code_) & FloatRegisters::VolatileMask);
     }
     const char* name() const {
         if (isDouble())
             return FloatRegisters::GetDoubleName(Encoding(code_));
         return FloatRegisters::GetSingleName(Encoding(code_));
     }
-    bool operator != (const VFPRegister& other) const {
-        return other.kind != kind || code_ != other.code_;
-    }
     bool aliases(const VFPRegister& other) {
         if (kind == other.kind)
             return code_ == other.code_;
         return doubleOverlay() == other.doubleOverlay();
     }
     static const int NumAliasedDoubles = 16;
     uint32_t numAliased() const {
         if (isDouble()) {
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -4836,18 +4836,16 @@ MacroAssembler::PushRegsInMask(LiveRegis
     adjustFrame(diffF);
     diffF += transferMultipleByRuns(set.fpus(), IsStore, StackPointer, DB);
     MOZ_ASSERT(diffF == 0);
 }
 
 void
 MacroAssembler::storeRegsInMask(LiveRegisterSet set, Address dest, Register scratch)
 {
-    MOZ_ASSERT(!set.has(scratch));
-
     int32_t diffF = set.fpus().getPushSizeInBytes();
     int32_t diffG = set.gprs().size() * sizeof(intptr_t);
 
     MOZ_ASSERT(dest.offset >= diffF + diffG);
 
     if (set.gprs().size() > 1) {
         computeEffectiveAddress(dest, scratch);
 
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -7208,54 +7208,49 @@ class LCallDeleteElement : public LCallI
     }
 
     MDeleteElement* mir() const {
         return mir_->toDeleteElement();
     }
 };
 
 // Patchable jump to stubs generated for a SetProperty cache.
-class LSetPropertyCache : public LInstructionHelper<0, 1 + 2 * BOX_PIECES, 4>
+class LSetPropertyCache : public LInstructionHelper<0, 1 + 2 * BOX_PIECES, 3>
 {
   public:
     LIR_HEADER(SetPropertyCache)
 
     LSetPropertyCache(const LAllocation& object, const LBoxAllocation& id,
                       const LBoxAllocation& value, const LDefinition& temp,
-                      const LDefinition& tempToUnboxIndex, const LDefinition& tempDouble,
-                      const LDefinition& tempFloat32) {
+                      const LDefinition& tempDouble, const LDefinition& tempFloat32) {
         setOperand(0, object);
         setBoxOperand(Id, id);
         setBoxOperand(Value, value);
         setTemp(0, temp);
-        setTemp(1, tempToUnboxIndex);
-        setTemp(2, tempDouble);
-        setTemp(3, tempFloat32);
+        setTemp(1, tempDouble);
+        setTemp(2, tempFloat32);
     }
 
     static const size_t Id = 1;
     static const size_t Value = 1 + BOX_PIECES;
 
     const MSetPropertyCache* mir() const {
         return mir_->toSetPropertyCache();
     }
 
     const LDefinition* temp() {
         return getTemp(0);
     }
-    const LDefinition* tempToUnboxIndex() {
+    const LDefinition* tempDouble() {
         return getTemp(1);
     }
-    const LDefinition* tempDouble() {
-        return getTemp(2);
-    }
     const LDefinition* tempFloat32() {
         if (hasUnaliasedDouble())
-            return getTemp(3);
-        return getTemp(2);
+            return getTemp(2);
+        return getTemp(1);
     }
 };
 
 class LCallIteratorStartV : public LCallInstructionHelper<1, BOX_PIECES, 0>
 {
   public:
     LIR_HEADER(CallIteratorStartV)
 
--- a/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp
+++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp
@@ -457,30 +457,25 @@ MacroAssembler::PushRegsInMask(LiveRegis
     MOZ_ASSERT(numFpu == 0);
     // x64 padding to keep the stack aligned on uintptr_t. Keep in sync with
     // GetPushBytesInSize.
     diffF -= diffF % sizeof(uintptr_t);
     MOZ_ASSERT(diffF == 0);
 }
 
 void
-MacroAssembler::storeRegsInMask(LiveRegisterSet set, Address dest, Register scratch)
+MacroAssembler::storeRegsInMask(LiveRegisterSet set, Address dest, Register)
 {
-    // We don't use |scratch| here, but assert this for other platforms.
-    MOZ_ASSERT(!set.has(scratch));
-
     FloatRegisterSet fpuSet(set.fpus().reduceSetForPush());
     unsigned numFpu = fpuSet.size();
     int32_t diffF = fpuSet.getPushSizeInBytes();
     int32_t diffG = set.gprs().size() * sizeof(intptr_t);
 
     MOZ_ASSERT(dest.offset >= diffG + diffF);
 
-    // On x86, always use push to push the integer registers, as it's fast
-    // on modern hardware and it's a small instruction.
     for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); ++iter) {
         diffG -= sizeof(intptr_t);
         dest.offset -= sizeof(intptr_t);
         storePtr(*iter, dest);
     }
     MOZ_ASSERT(diffG == 0);
 
     for (FloatRegisterBackwardIterator iter(fpuSet); iter.more(); ++iter) {