Bug 1091329 - Optimize writes to reference members of TypedObjects, r=nmatsakis,jandem.
authorBrian Hackett <bhackett1024@gmail.com>
Tue, 04 Nov 2014 18:21:47 -0700
changeset 240460 4bdc3391644e7200cb8419cdf59e502f36424498
parent 240459 585a22940ba75968087f66c291d331f3cc03982b
child 240461 f86dbba73d43d8f87eb93a1b218397e0f22a8f53
push id660
push userraliiev@mozilla.com
push dateWed, 18 Feb 2015 20:30:48 +0000
treeherdermozilla-release@49e493494178 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnmatsakis, jandem
bugs1091329
milestone36.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1091329 - Optimize writes to reference members of TypedObjects, r=nmatsakis,jandem.
js/src/jit-test/tests/TypedObject/jit-write-references.js
js/src/jit/CodeGenerator.cpp
js/src/jit/CodeGenerator.h
js/src/jit/Ion.cpp
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
js/src/jit/IonTypes.h
js/src/jit/JitCompartment.h
js/src/jit/LIR-Common.h
js/src/jit/LIR.h
js/src/jit/LOpcodes.h
js/src/jit/Lowering.cpp
js/src/jit/Lowering.h
js/src/jit/MIR.h
js/src/jit/MOpcodes.h
js/src/jit/ParallelSafetyAnalysis.cpp
js/src/jit/TypePolicy.cpp
js/src/jit/TypePolicy.h
js/src/jit/VMFunctions.cpp
js/src/jit/VMFunctions.h
js/src/jit/arm/MacroAssembler-arm.cpp
js/src/jit/arm/MacroAssembler-arm.h
js/src/jit/mips/MacroAssembler-mips.cpp
js/src/jit/mips/MacroAssembler-mips.h
js/src/jit/x64/MacroAssembler-x64.h
js/src/jit/x86/MacroAssembler-x86.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/TypedObject/jit-write-references.js
@@ -0,0 +1,83 @@
+if (typeof TypedObject === "undefined")
+    quit();
+
+var T = TypedObject;
+
+var ObjectStruct = new T.StructType({f: T.Object});
+var StringStruct = new T.StructType({f: T.string});
+var ValueStruct = new T.StructType({f: T.Any});
+
+// Suppress ion compilation of the global script.
+with({}){}
+
+var o = new ObjectStruct();
+var s = new StringStruct();
+var v = new ValueStruct();
+
+// Make sure that unboxed null pointers on the stack are marked correctly.
+whatever = new Array();
+function testGC(o, p) {
+    for (var i = 0; i < 5; i++) {
+        minorgc();
+        o.f = p;
+        whatever.push(new Array()); // minorgc needs garbage before it scans the stack.
+    }
+}
+testGC(o, {});
+testGC(o, null);
+
+// Test writing various things to an object field.
+function writeObject(o, v, expected) {
+    o.f = v;
+    assertEq(typeof o.f, "object");
+    assertEq("" + o.f, expected);
+}
+for (var i = 0; i < 5; i++)
+    writeObject(o, {toString: function() { return "helo"} }, "helo");
+for (var i = 0; i < 5; i++)
+    writeObject(o, null, "null");
+for (var i = 0; i < 5; i++)
+    writeObject(o, "three", "three");
+for (var i = 0; i < 5; i++)
+    writeObject(o, 4.5, "4.5");
+for (var i = 0; i < 5; i++) {
+    try {
+        writeObject(o, undefined, "");
+    } catch (e) {
+        assertEq(e instanceof TypeError, true);
+    }
+}
+
+// Test writing various things to a string field.
+function writeString(o, v, expected) {
+    o.f = v;
+    assertEq(typeof o.f, "string");
+    assertEq("" + o.f, expected);
+}
+for (var i = 0; i < 5; i++)
+    writeString(s, {toString: function() { return "helo"} }, "helo");
+for (var i = 0; i < 5; i++)
+    writeString(s, null, "null");
+for (var i = 0; i < 5; i++)
+    writeString(s, "three", "three");
+for (var i = 0; i < 5; i++)
+    writeString(s, 4.5, "4.5");
+for (var i = 0; i < 5; i++)
+    writeString(s, undefined, "undefined");
+
+// Test writing various things to a value field.
+function writeValue(o, v, expectedType, expected) {
+    o.f = v;
+    assertEq(typeof o.f, expectedType);
+    assertEq("" + o.f, expected);
+}
+for (var i = 0; i < 5; i++)
+    writeValue(v, {toString: function() { return "helo"} }, "object", "helo");
+for (var i = 0; i < 5; i++)
+    writeValue(v, null, "object", "null");
+for (var i = 0; i < 5; i++)
+    writeValue(v, "three", "string", "three");
+for (var i = 0; i < 5; i++)
+    writeValue(v, 4.5, "number", "4.5");
+for (var i = 0; i < 5; i++)
+    writeValue(v, undefined, "undefined", "undefined");
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -999,16 +999,41 @@ CodeGenerator::visitValueToString(LValue
     masm.assumeUnreachable("Unexpected type for MValueToString.");
 #endif
 
     masm.bind(&done);
     masm.bind(ool->rejoin());
     return true;
 }
 
+typedef JSObject *(*ToObjectFn)(JSContext *, HandleValue, bool);
+static const VMFunction ToObjectInfo = FunctionInfo<ToObjectFn>(ToObjectSlow);
+
+bool
+CodeGenerator::visitValueToObjectOrNull(LValueToObjectOrNull *lir)
+{
+    ValueOperand input = ToValue(lir, LValueToObjectOrNull::Input);
+    Register output = ToRegister(lir->output());
+
+    OutOfLineCode *ool = oolCallVM(ToObjectInfo, lir, (ArgList(), input, Imm32(0)),
+                                   StoreRegisterTo(output));
+    if (!ool)
+        return false;
+
+    Label done;
+    masm.branchTestObject(Assembler::Equal, input, &done);
+    masm.branchTestNull(Assembler::NotEqual, input, ool->entry());
+
+    masm.bind(&done);
+    masm.unboxNonDouble(input, output);
+
+    masm.bind(ool->rejoin());
+    return true;
+}
+
 typedef JSObject *(*CloneRegExpObjectFn)(JSContext *, JSObject *);
 static const VMFunction CloneRegExpObjectInfo =
     FunctionInfo<CloneRegExpObjectFn>(CloneRegExpObject);
 
 bool
 CodeGenerator::visitRegExp(LRegExp *lir)
 {
     pushArg(ImmGCPtr(lir->mir()->source()));
@@ -3914,22 +3939,24 @@ CodeGenerator::emitObjectOrStringResultC
     masm.push(temp);
 
     // Don't check if the script has been invalidated. In that case invalid
     // types are expected (until we reach the OsiPoint and bailout).
     Label done;
     if (!branchIfInvalidated(temp, &done))
         return false;
 
-    if (mir->type() == MIRType_Object &&
+    if ((mir->type() == MIRType_Object || mir->type() == MIRType_ObjectOrNull) &&
         mir->resultTypeSet() &&
         !mir->resultTypeSet()->unknownObject())
     {
         // We have a result TypeSet, assert this object is in it.
         Label miss, ok;
+        if (mir->type() == MIRType_ObjectOrNull)
+            masm.branchPtr(Assembler::NotEqual, output, ImmWord(0), &ok);
         if (mir->resultTypeSet()->getObjectCount() > 0)
             masm.guardObjectType(output, mir->resultTypeSet(), temp, &miss);
         else
             masm.jump(&miss);
         masm.jump(&ok);
 
         masm.bind(&miss);
         masm.assumeUnreachable("MIR instruction returned object with unexpected type");
@@ -3939,21 +3966,36 @@ CodeGenerator::emitObjectOrStringResultC
 
     // Check that we have a valid GC pointer.
     if (gen->info().executionMode() != ParallelExecution) {
         saveVolatile();
         masm.setupUnalignedABICall(2, temp);
         masm.loadJSContext(temp);
         masm.passABIArg(temp);
         masm.passABIArg(output);
-        masm.callWithABINoProfiling(mir->type() == MIRType_Object
-                                    ? JS_FUNC_TO_DATA_PTR(void *, AssertValidObjectPtr)
-                                    : mir->type() == MIRType_String
-                                      ? JS_FUNC_TO_DATA_PTR(void *, AssertValidStringPtr)
-                                      : JS_FUNC_TO_DATA_PTR(void *, AssertValidSymbolPtr));
+
+        void *callee;
+        switch (mir->type()) {
+          case MIRType_Object:
+            callee = JS_FUNC_TO_DATA_PTR(void *, AssertValidObjectPtr);
+            break;
+          case MIRType_ObjectOrNull:
+            callee = JS_FUNC_TO_DATA_PTR(void *, AssertValidObjectOrNullPtr);
+            break;
+          case MIRType_String:
+            callee = JS_FUNC_TO_DATA_PTR(void *, AssertValidStringPtr);
+            break;
+          case MIRType_Symbol:
+            callee = JS_FUNC_TO_DATA_PTR(void *, AssertValidSymbolPtr);
+            break;
+          default:
+            MOZ_CRASH();
+        }
+
+        masm.callWithABINoProfiling(callee);
         restoreVolatile();
     }
 
     masm.bind(&done);
     masm.pop(temp);
     return true;
 }
 
@@ -4023,16 +4065,17 @@ CodeGenerator::emitDebugResultChecks(LIn
     // In debug builds, check that LIR instructions return valid values.
 
     MDefinition *mir = ins->mirRaw();
     if (!mir)
         return true;
 
     switch (mir->type()) {
       case MIRType_Object:
+      case MIRType_ObjectOrNull:
       case MIRType_String:
       case MIRType_Symbol:
         return emitObjectOrStringResultChecks(ins, mir);
       case MIRType_Value:
         return emitValueResultChecks(ins, mir);
       default:
         return true;
     }
@@ -6784,16 +6827,55 @@ CodeGenerator::visitOutOfLineStoreElemen
     if (!callVM(SetDenseElementInfo, ins))
         return false;
 
     restoreLive(ins);
     masm.jump(ool->rejoin());
     return true;
 }
 
+template <typename T>
+static void
+StoreUnboxedPointer(MacroAssembler &masm, T address, MIRType type, const LAllocation *value)
+{
+    masm.patchableCallPreBarrier(address, type);
+    if (value->isConstant()) {
+        Value v = *value->toConstant();
+        if (v.isMarkable()) {
+            masm.storePtr(ImmGCPtr(v.toGCThing()), address);
+        } else {
+            MOZ_ASSERT(v.isNull());
+            masm.storePtr(ImmWord(0), address);
+        }
+    } else {
+        masm.storePtr(ToRegister(value), address);
+    }
+}
+
+bool
+CodeGenerator::visitStoreUnboxedPointer(LStoreUnboxedPointer *lir)
+{
+    MOZ_ASSERT(lir->mir()->isStoreUnboxedObjectOrNull() || lir->mir()->isStoreUnboxedString());
+    MIRType type = lir->mir()->isStoreUnboxedObjectOrNull() ? MIRType_Object : MIRType_String;
+
+    Register elements = ToRegister(lir->elements());
+    const LAllocation *index = lir->index();
+    const LAllocation *value = lir->value();
+
+    if (index->isConstant()) {
+        Address address(elements, ToInt32(index) * sizeof(uintptr_t));
+        StoreUnboxedPointer(masm, address, type, value);
+    } else {
+        BaseIndex address(elements, ToRegister(index), ScalePointer);
+        StoreUnboxedPointer(masm, address, type, value);
+    }
+
+    return true;
+}
+
 typedef bool (*ArrayPopShiftFn)(JSContext *, HandleObject, MutableHandleValue);
 static const VMFunction ArrayPopDenseInfo = FunctionInfo<ArrayPopShiftFn>(jit::ArrayPopDense);
 static const VMFunction ArrayShiftDenseInfo = FunctionInfo<ArrayPopShiftFn>(jit::ArrayShiftDense);
 
 bool
 CodeGenerator::emitArrayPopShift(LInstruction *lir, const MArrayPopShift *mir, Register obj,
                                  Register elementsTemp, Register lengthTemp, TypedOrValueRegister out)
 {
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -95,16 +95,17 @@ class CodeGenerator : public CodeGenerat
     bool visitTestVAndBranch(LTestVAndBranch *lir);
     bool visitFunctionDispatch(LFunctionDispatch *lir);
     bool visitTypeObjectDispatch(LTypeObjectDispatch *lir);
     bool visitBooleanToString(LBooleanToString *lir);
     void emitIntToString(Register input, Register output, Label *ool);
     bool visitIntToString(LIntToString *lir);
     bool visitDoubleToString(LDoubleToString *lir);
     bool visitValueToString(LValueToString *lir);
+    bool visitValueToObjectOrNull(LValueToObjectOrNull *lir);
     bool visitInteger(LInteger *lir);
     bool visitRegExp(LRegExp *lir);
     bool visitRegExpExec(LRegExpExec *lir);
     bool visitOutOfLineRegExpExec(OutOfLineRegExpExec *ool);
     bool visitRegExpTest(LRegExpTest *lir);
     bool visitOutOfLineRegExpTest(OutOfLineRegExpTest *ool);
     bool visitRegExpReplace(LRegExpReplace *lir);
     bool visitStringReplace(LStringReplace *lir);
@@ -247,16 +248,17 @@ class CodeGenerator : public CodeGenerat
     template<typename T> bool emitLoadElementT(LLoadElementT *lir, const T &source);
     bool visitLoadElementT(LLoadElementT *lir);
     bool visitLoadElementV(LLoadElementV *load);
     bool visitLoadElementHole(LLoadElementHole *lir);
     bool visitStoreElementT(LStoreElementT *lir);
     bool visitStoreElementV(LStoreElementV *lir);
     bool visitStoreElementHoleT(LStoreElementHoleT *lir);
     bool visitStoreElementHoleV(LStoreElementHoleV *lir);
+    bool visitStoreUnboxedPointer(LStoreUnboxedPointer *lir);
     bool emitArrayPopShift(LInstruction *lir, const MArrayPopShift *mir, Register obj,
                            Register elementsTemp, Register lengthTemp, TypedOrValueRegister out);
     bool visitArrayPopShiftV(LArrayPopShiftV *lir);
     bool visitArrayPopShiftT(LArrayPopShiftT *lir);
     bool emitArrayPush(LInstruction *lir, const MArrayPush *mir, Register obj,
                        ConstantOrRegister value, Register elementsTemp, Register length);
     bool visitArrayPushV(LArrayPushV *lir);
     bool visitArrayPushT(LArrayPushT *lir);
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -276,16 +276,21 @@ JitRuntime::initialize(JSContext *cx)
     if (!valuePreBarrier_)
         return false;
 
     JitSpew(JitSpew_Codegen, "# Emitting Pre Barrier for String");
     stringPreBarrier_ = generatePreBarrier(cx, MIRType_String);
     if (!stringPreBarrier_)
         return false;
 
+    JitSpew(JitSpew_Codegen, "# Emitting Pre Barrier for Object");
+    objectPreBarrier_ = generatePreBarrier(cx, MIRType_Object);
+    if (!objectPreBarrier_)
+        return false;
+
     JitSpew(JitSpew_Codegen, "# Emitting Pre Barrier for Shape");
     shapePreBarrier_ = generatePreBarrier(cx, MIRType_Shape);
     if (!shapePreBarrier_)
         return false;
 
     JitSpew(JitSpew_Codegen, "# Emitting Pre Barrier for TypeObject");
     typeObjectPreBarrier_ = generatePreBarrier(cx, MIRType_TypeObject);
     if (!typeObjectPreBarrier_)
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -8147,36 +8147,63 @@ IonBuilder::setElemTryTypedObject(bool *
         return true;
 
     switch (elemPrediction.kind()) {
       case type::Simd:
         // FIXME (bug 894105): store a MIRType_float32x4 etc
         return true;
 
       case type::Reference:
-      case type::Struct:
-      case type::SizedArray:
-      case type::UnsizedArray:
-        // For now, only optimize storing scalars.
-        return true;
+        return setElemTryReferenceElemOfTypedObject(emitted, obj, index,
+                                                    objPrediction, value, elemPrediction);
 
       case type::Scalar:
         return setElemTryScalarElemOfTypedObject(emitted,
                                                  obj,
                                                  index,
                                                  objPrediction,
                                                  value,
                                                  elemPrediction,
                                                  elemSize);
+
+      case type::Struct:
+      case type::SizedArray:
+      case type::UnsizedArray:
+        // Not yet optimized.
+        return true;
     }
 
     MOZ_CRASH("Bad kind");
 }
 
 bool
+IonBuilder::setElemTryReferenceElemOfTypedObject(bool *emitted,
+                                                 MDefinition *obj,
+                                                 MDefinition *index,
+                                                 TypedObjectPrediction objPrediction,
+                                                 MDefinition *value,
+                                                 TypedObjectPrediction elemPrediction)
+{
+    ReferenceTypeDescr::Type elemType = elemPrediction.referenceType();
+    size_t elemSize = ReferenceTypeDescr::size(elemType);
+
+    MDefinition *indexAsByteOffset;
+    if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objPrediction, &indexAsByteOffset))
+        return true;
+
+    if (!storeReferenceTypedObjectValue(obj, indexAsByteOffset, elemType, value))
+        return false;
+
+    current->push(value);
+
+    *emitted = true;
+    return true;
+}
+
+bool
 IonBuilder::setElemTryScalarElemOfTypedObject(bool *emitted,
                                               MDefinition *obj,
                                               MDefinition *index,
                                               TypedObjectPrediction objPrediction,
                                               MDefinition *value,
                                               TypedObjectPrediction elemPrediction,
                                               int32_t elemSize)
 {
@@ -10042,31 +10069,55 @@ IonBuilder::setPropTryTypedObject(bool *
         return true;
 
     switch (fieldPrediction.kind()) {
       case type::Simd:
         // FIXME (bug 894104): store into a MIRType_float32x4 etc
         return true;
 
       case type::Reference:
-      case type::Struct:
-      case type::SizedArray:
-      case type::UnsizedArray:
-        // For now, only optimize storing scalars.
-        return true;
+        return setPropTryReferencePropOfTypedObject(emitted, obj, fieldOffset,
+                                                    value, fieldPrediction);
 
       case type::Scalar:
         return setPropTryScalarPropOfTypedObject(emitted, obj, fieldOffset,
                                                  value, fieldPrediction);
+
+      case type::Struct:
+      case type::SizedArray:
+      case type::UnsizedArray:
+        return true;
     }
 
     MOZ_CRASH("Unknown kind");
 }
 
 bool
+IonBuilder::setPropTryReferencePropOfTypedObject(bool *emitted,
+                                                 MDefinition *obj,
+                                                 int32_t fieldOffset,
+                                                 MDefinition *value,
+                                                 TypedObjectPrediction fieldPrediction)
+{
+    ReferenceTypeDescr::Type fieldType = fieldPrediction.referenceType();
+
+    types::TypeObjectKey *globalType = types::TypeObjectKey::get(&script()->global());
+    if (globalType->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED))
+        return true;
+
+    if (!storeReferenceTypedObjectValue(obj, constantInt(fieldOffset), fieldType, value))
+        return false;
+
+    current->push(value);
+
+    *emitted = true;
+    return true;
+}
+
+bool
 IonBuilder::setPropTryScalarPropOfTypedObject(bool *emitted,
                                               MDefinition *obj,
                                               int32_t fieldOffset,
                                               MDefinition *value,
                                               TypedObjectPrediction fieldPrediction)
 {
     // Must always be loading the same scalar type
     Scalar::Type fieldType = fieldPrediction.scalarType();
@@ -11192,16 +11243,47 @@ IonBuilder::storeScalarTypedObjectValue(
                                      type);
     if (racy)
         store->setRacy();
     current->add(store);
 
     return true;
 }
 
+bool
+IonBuilder::storeReferenceTypedObjectValue(MDefinition *typedObj,
+                                           MDefinition *byteOffset,
+                                           ReferenceTypeDescr::Type type,
+                                           MDefinition *value)
+{
+    if (NeedsPostBarrier(info(), value))
+        current->add(MPostWriteBarrier::New(alloc(), typedObj, value));
+
+    // Find location within the owner object.
+    MDefinition *elements, *scaledOffset;
+    size_t alignment = ReferenceTypeDescr::alignment(type);
+    loadTypedObjectElements(typedObj, byteOffset, alignment, &elements, &scaledOffset);
+
+    MInstruction *store;
+    switch (type) {
+      case ReferenceTypeDescr::TYPE_ANY:
+        store = MStoreElement::New(alloc(), elements, scaledOffset, value, false);
+        break;
+      case ReferenceTypeDescr::TYPE_OBJECT:
+        store = MStoreUnboxedObjectOrNull::New(alloc(), elements, scaledOffset, value);
+        break;
+      case ReferenceTypeDescr::TYPE_STRING:
+        store = MStoreUnboxedString::New(alloc(), elements, scaledOffset, value);
+        break;
+    }
+
+    current->add(store);
+    return true;
+}
+
 MConstant *
 IonBuilder::constant(const Value &v)
 {
     MConstant *c = MConstant::New(alloc(), v, constraints());
     current->add(c);
     return c;
 }
 
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -446,16 +446,21 @@ class IonBuilder
     bool setPropTryDefiniteSlot(bool *emitted, MDefinition *obj,
                                 PropertyName *name, MDefinition *value,
                                 types::TemporaryTypeSet *objTypes);
     bool setPropTryInlineAccess(bool *emitted, MDefinition *obj,
                                 PropertyName *name, MDefinition *value,
                                 types::TemporaryTypeSet *objTypes);
     bool setPropTryTypedObject(bool *emitted, MDefinition *obj,
                                PropertyName *name, MDefinition *value);
+    bool setPropTryReferencePropOfTypedObject(bool *emitted,
+                                              MDefinition *obj,
+                                              int32_t fieldOffset,
+                                              MDefinition *value,
+                                              TypedObjectPrediction fieldPrediction);
     bool setPropTryScalarPropOfTypedObject(bool *emitted,
                                            MDefinition *obj,
                                            int32_t fieldOffset,
                                            MDefinition *value,
                                            TypedObjectPrediction fieldTypeReprs);
     bool setPropTryCache(bool *emitted, MDefinition *obj,
                          PropertyName *name, MDefinition *value,
                          bool barrier, types::TemporaryTypeSet *objTypes);
@@ -476,16 +481,20 @@ class IonBuilder
     void loadTypedObjectElements(MDefinition *typedObj,
                                  MDefinition *offset,
                                  int32_t unit,
                                  MDefinition **ownerElements,
                                  MDefinition **ownerScaledOffset);
     MDefinition *typeObjectForElementFromArrayStructType(MDefinition *typedObj);
     MDefinition *typeObjectForFieldFromStructType(MDefinition *type,
                                                   size_t fieldIndex);
+    bool storeReferenceTypedObjectValue(MDefinition *typedObj,
+                                        MDefinition *byteOffset,
+                                        ReferenceTypeDescr::Type type,
+                                        MDefinition *value);
     bool storeScalarTypedObjectValue(MDefinition *typedObj,
                                      MDefinition *offset,
                                      ScalarTypeDescr::Type type,
                                      bool racy,
                                      MDefinition *value);
     bool checkTypedObjectIndexInBounds(int32_t elemSize,
                                        MDefinition *obj,
                                        MDefinition *index,
@@ -510,16 +519,22 @@ class IonBuilder
     bool setElemTryTypedStatic(bool *emitted, MDefinition *object,
                                MDefinition *index, MDefinition *value);
     bool setElemTryDense(bool *emitted, MDefinition *object,
                          MDefinition *index, MDefinition *value);
     bool setElemTryArguments(bool *emitted, MDefinition *object,
                              MDefinition *index, MDefinition *value);
     bool setElemTryCache(bool *emitted, MDefinition *object,
                          MDefinition *index, MDefinition *value);
+    bool setElemTryReferenceElemOfTypedObject(bool *emitted,
+                                              MDefinition *obj,
+                                              MDefinition *index,
+                                              TypedObjectPrediction objPrediction,
+                                              MDefinition *value,
+                                              TypedObjectPrediction elemPrediction);
     bool setElemTryScalarElemOfTypedObject(bool *emitted,
                                            MDefinition *obj,
                                            MDefinition *index,
                                            TypedObjectPrediction objTypeReprs,
                                            MDefinition *value,
                                            TypedObjectPrediction elemTypeReprs,
                                            int32_t elemSize);
 
--- a/js/src/jit/IonTypes.h
+++ b/js/src/jit/IonTypes.h
@@ -364,16 +364,17 @@ enum MIRType
     MIRType_Symbol,
     MIRType_Object,
     MIRType_MagicOptimizedArguments,   // JS_OPTIMIZED_ARGUMENTS magic value.
     MIRType_MagicOptimizedOut,         // JS_OPTIMIZED_OUT magic value.
     MIRType_MagicHole,                 // JS_ELEMENTS_HOLE magic value.
     MIRType_MagicIsConstructing,       // JS_IS_CONSTRUCTING magic value.
     MIRType_MagicUninitializedLexical, // JS_UNINITIALIZED_LEXICAL magic value.
     MIRType_Value,
+    MIRType_ObjectOrNull,
     MIRType_None,                      // Invalid, used as a placeholder.
     MIRType_Slots,                     // A slots vector
     MIRType_Elements,                  // An elements vector
     MIRType_Pointer,                   // An opaque pointer that receives no special treatment
     MIRType_Shape,                     // A Shape pointer.
     MIRType_TypeObject,                // A TypeObject pointer.
     MIRType_ForkJoinContext,           // js::ForkJoinContext*
     MIRType_Last = MIRType_ForkJoinContext,
--- a/js/src/jit/JitCompartment.h
+++ b/js/src/jit/JitCompartment.h
@@ -182,16 +182,17 @@ class JitRuntime
     JitCode *parallelArgumentsRectifier_;
 
     // Thunk that invalides an (Ion compiled) caller on the Ion stack.
     JitCode *invalidator_;
 
     // Thunk that calls the GC pre barrier.
     JitCode *valuePreBarrier_;
     JitCode *stringPreBarrier_;
+    JitCode *objectPreBarrier_;
     JitCode *shapePreBarrier_;
     JitCode *typeObjectPreBarrier_;
 
     // Thunk to call malloc/free.
     JitCode *mallocStub_;
     JitCode *freeStub_;
 
     // Thunk called to finish compilation of an IonScript.
@@ -356,16 +357,17 @@ class JitRuntime
     EnterJitCode enterBaseline() const {
         return enterBaselineJIT_->as<EnterJitCode>();
     }
 
     JitCode *preBarrier(MIRType type) const {
         switch (type) {
           case MIRType_Value: return valuePreBarrier_;
           case MIRType_String: return stringPreBarrier_;
+          case MIRType_Object: return objectPreBarrier_;
           case MIRType_Shape: return shapePreBarrier_;
           case MIRType_TypeObject: return typeObjectPreBarrier_;
           default: MOZ_CRASH();
         }
     }
 
     JitCode *mallocStub() const {
         return mallocStub_;
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -3710,16 +3710,32 @@ class LValueToString : public LInstructi
         return mir_->toToString();
     }
 
     const LDefinition *tempToUnbox() {
         return getTemp(0);
     }
 };
 
+// Convert a value to an object or null pointer.
+class LValueToObjectOrNull : public LInstructionHelper<1, BOX_PIECES, 0>
+{
+  public:
+    LIR_HEADER(ValueToObjectOrNull)
+
+    explicit LValueToObjectOrNull()
+    {}
+
+    static const size_t Input = 0;
+
+    const MToObjectOrNull *mir() {
+        return mir_->toToObjectOrNull();
+    }
+};
+
 class LInt32x4ToFloat32x4 : public LInstructionHelper<1, 1, 0>
 {
   public:
     LIR_HEADER(Int32x4ToFloat32x4);
     explicit LInt32x4ToFloat32x4(const LAllocation &input) {
         setOperand(0, input);
     }
 };
@@ -4603,16 +4619,43 @@ class LStoreElementHoleT : public LInstr
     const LAllocation *index() {
         return getOperand(2);
     }
     const LAllocation *value() {
         return getOperand(3);
     }
 };
 
+class LStoreUnboxedPointer : public LInstructionHelper<0, 3, 0>
+{
+  public:
+    LIR_HEADER(StoreUnboxedPointer)
+
+    LStoreUnboxedPointer(LAllocation elements, LAllocation index, LAllocation value) {
+        setOperand(0, elements);
+        setOperand(1, index);
+        setOperand(2, value);
+    }
+
+    MDefinition *mir() {
+        MOZ_ASSERT(mir_->isStoreUnboxedObjectOrNull() || mir_->isStoreUnboxedString());
+        return mir_;
+    }
+
+    const LAllocation *elements() {
+        return getOperand(0);
+    }
+    const LAllocation *index() {
+        return getOperand(1);
+    }
+    const LAllocation *value() {
+        return getOperand(2);
+    }
+};
+
 class LArrayPopShiftV : public LInstructionHelper<BOX_PIECES, 1, 2>
 {
   public:
     LIR_HEADER(ArrayPopShiftV)
 
     LArrayPopShiftV(const LAllocation &object, const LDefinition &temp0, const LDefinition &temp1) {
         setOperand(0, object);
         setTemp(0, temp0);
--- a/js/src/jit/LIR.h
+++ b/js/src/jit/LIR.h
@@ -549,16 +549,17 @@ class LDefinition
           case MIRType_Int32:
             // The stack slot allocator doesn't currently support allocating
             // 1-byte slots, so for now we lower MIRType_Boolean into INT32.
             static_assert(sizeof(bool) <= sizeof(int32_t), "bool doesn't fit in an int32 slot");
             return LDefinition::INT32;
           case MIRType_String:
           case MIRType_Symbol:
           case MIRType_Object:
+          case MIRType_ObjectOrNull:
             return LDefinition::OBJECT;
           case MIRType_Double:
             return LDefinition::DOUBLE;
           case MIRType_Float32:
             return LDefinition::FLOAT32;
 #if defined(JS_PUNBOX64)
           case MIRType_Value:
             return LDefinition::BOX;
--- a/js/src/jit/LOpcodes.h
+++ b/js/src/jit/LOpcodes.h
@@ -168,16 +168,17 @@
     _(DoubleToInt32)                \
     _(Float32ToInt32)               \
     _(TruncateDToInt32)             \
     _(TruncateFToInt32)             \
     _(BooleanToString)              \
     _(IntToString)                  \
     _(DoubleToString)               \
     _(ValueToString)                \
+    _(ValueToObjectOrNull)          \
     _(Int32x4ToFloat32x4)           \
     _(Float32x4ToInt32x4)           \
     _(Start)                        \
     _(OsrEntry)                     \
     _(OsrValue)                     \
     _(OsrScopeChain)                \
     _(OsrReturnValue)               \
     _(OsrArgumentsObject)           \
@@ -216,16 +217,17 @@
     _(BoundsCheck)                  \
     _(BoundsCheckRange)             \
     _(BoundsCheckLower)             \
     _(LoadElementV)                 \
     _(LoadElementT)                 \
     _(LoadElementHole)              \
     _(StoreElementV)                \
     _(StoreElementT)                \
+    _(StoreUnboxedPointer)          \
     _(ArrayPopShiftV)               \
     _(ArrayPopShiftT)               \
     _(ArrayPushV)                   \
     _(ArrayPushT)                   \
     _(ArrayConcat)                  \
     _(ArrayJoin)                    \
     _(StoreElementHoleV)            \
     _(StoreElementHoleT)            \
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2033,16 +2033,27 @@ LIRGenerator::visitToString(MToString *i
       }
 
       default:
         // Float32, symbols, and objects are not supported.
         MOZ_CRASH("unexpected type");
     }
 }
 
+bool
+LIRGenerator::visitToObjectOrNull(MToObjectOrNull *ins)
+{
+    MOZ_ASSERT(ins->input()->type() == MIRType_Value);
+
+    LValueToObjectOrNull *lir = new(alloc()) LValueToObjectOrNull();
+    if (!useBox(lir, LValueToString::Input, ins->input()))
+        return false;
+    return define(lir, ins) && assignSafepoint(lir, ins);
+}
+
 static bool
 MustCloneRegExpForCall(MCall *call, uint32_t useIndex)
 {
     // We have a regex literal flowing into a call. Return |false| iff
     // this is a native call that does not let the regex escape.
 
     JSFunction *target = call->getSingleTarget();
     if (!target || !target->isNative())
@@ -2770,16 +2781,48 @@ LIRGenerator::visitStoreElementHole(MSto
         break;
       }
     }
 
     return add(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
+LIRGenerator::visitStoreUnboxedObjectOrNull(MStoreUnboxedObjectOrNull *ins)
+{
+    MOZ_ASSERT(ins->elements()->type() == MIRType_Elements);
+    MOZ_ASSERT(ins->index()->type() == MIRType_Int32);
+    MOZ_ASSERT(ins->value()->type() == MIRType_Object ||
+               ins->value()->type() == MIRType_Null ||
+               ins->value()->type() == MIRType_ObjectOrNull);
+
+    const LUse elements = useRegister(ins->elements());
+    const LAllocation index = useRegisterOrNonDoubleConstant(ins->index());
+    const LAllocation value = useRegisterOrNonDoubleConstant(ins->value());
+
+    LInstruction *lir = new(alloc()) LStoreUnboxedPointer(elements, index, value);
+    return add(lir, ins);
+}
+
+bool
+LIRGenerator::visitStoreUnboxedString(MStoreUnboxedString *ins)
+{
+    MOZ_ASSERT(ins->elements()->type() == MIRType_Elements);
+    MOZ_ASSERT(ins->index()->type() == MIRType_Int32);
+    MOZ_ASSERT(ins->value()->type() == MIRType_String);
+
+    const LUse elements = useRegister(ins->elements());
+    const LAllocation index = useRegisterOrConstant(ins->index());
+    const LAllocation value = useRegisterOrNonDoubleConstant(ins->value());
+
+    LInstruction *lir = new(alloc()) LStoreUnboxedPointer(elements, index, value);
+    return add(lir, ins);
+}
+
+bool
 LIRGenerator::visitEffectiveAddress(MEffectiveAddress *ins)
 {
     return define(new(alloc()) LEffectiveAddress(useRegister(ins->base()), useRegister(ins->index())), ins);
 }
 
 bool
 LIRGenerator::visitArrayPopShift(MArrayPopShift *ins)
 {
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -154,16 +154,17 @@ class LIRGenerator : public LIRGenerator
     bool visitOsrScopeChain(MOsrScopeChain *object);
     bool visitOsrReturnValue(MOsrReturnValue *value);
     bool visitOsrArgumentsObject(MOsrArgumentsObject *object);
     bool visitToDouble(MToDouble *convert);
     bool visitToFloat32(MToFloat32 *convert);
     bool visitToInt32(MToInt32 *convert);
     bool visitTruncateToInt32(MTruncateToInt32 *truncate);
     bool visitToString(MToString *convert);
+    bool visitToObjectOrNull(MToObjectOrNull *convert);
     bool visitRegExp(MRegExp *ins);
     bool visitRegExpExec(MRegExpExec *ins);
     bool visitRegExpTest(MRegExpTest *ins);
     bool visitRegExpReplace(MRegExpReplace *ins);
     bool visitStringReplace(MStringReplace *ins);
     bool visitLambda(MLambda *ins);
     bool visitLambdaArrow(MLambdaArrow *ins);
     bool visitLambdaPar(MLambdaPar *ins);
@@ -197,16 +198,18 @@ class LIRGenerator : public LIRGenerator
     bool visitSetInitializedLength(MSetInitializedLength *ins);
     bool visitNot(MNot *ins);
     bool visitBoundsCheck(MBoundsCheck *ins);
     bool visitBoundsCheckLower(MBoundsCheckLower *ins);
     bool visitLoadElement(MLoadElement *ins);
     bool visitLoadElementHole(MLoadElementHole *ins);
     bool visitStoreElement(MStoreElement *ins);
     bool visitStoreElementHole(MStoreElementHole *ins);
+    bool visitStoreUnboxedObjectOrNull(MStoreUnboxedObjectOrNull *ins);
+    bool visitStoreUnboxedString(MStoreUnboxedString *ins);
     bool visitEffectiveAddress(MEffectiveAddress *ins);
     bool visitArrayPopShift(MArrayPopShift *ins);
     bool visitArrayPush(MArrayPush *ins);
     bool visitArrayConcat(MArrayConcat *ins);
     bool visitArrayJoin(MArrayJoin *ins);
     bool visitLoadTypedArrayElement(MLoadTypedArrayElement *ins);
     bool visitLoadTypedArrayElementHole(MLoadTypedArrayElementHole *ins);
     bool visitLoadTypedArrayElementStatic(MLoadTypedArrayElementStatic *ins);
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -263,20 +263,22 @@ class MNode : public TempObject
 class AliasSet {
   private:
     uint32_t flags_;
 
   public:
     enum Flag {
         None_             = 0,
         ObjectFields      = 1 << 0, // shape, class, slots, length etc.
-        Element           = 1 << 1, // A member of obj->elements.
+        Element           = 1 << 1, // A member of obj->elements, or reference
+                                    // typed object field.
         DynamicSlot       = 1 << 2, // A member of obj->slots.
         FixedSlot         = 1 << 3, // A member of obj->fixedSlots().
-        TypedArrayElement = 1 << 4, // A typed array element.
+        TypedArrayElement = 1 << 4, // A typed array element, or scalar typed
+                                    // object field.
         DOMProperty       = 1 << 5, // A DOM property
         FrameArgument     = 1 << 6, // An argument kept on the stack frame
         AsmJSGlobalVar    = 1 << 7, // An asm.js global var
         AsmJSHeap         = 1 << 8, // An asm.js heap load
         TypedArrayLength  = 1 << 9,// A typed array's length
         Last              = TypedArrayLength,
         Any               = Last | (Last - 1),
 
@@ -4637,16 +4639,46 @@ class MToString :
 
     bool fallible() const {
         return input()->mightBeType(MIRType_Object);
     }
 
     ALLOW_CLONE(MToString)
 };
 
+// Converts any type to an object or null value, throwing on undefined.
+class MToObjectOrNull :
+  public MUnaryInstruction,
+  public BoxInputsPolicy::Data
+{
+    explicit MToObjectOrNull(MDefinition *def)
+      : MUnaryInstruction(def)
+    {
+        setResultType(MIRType_ObjectOrNull);
+        setMovable();
+    }
+
+  public:
+    INSTRUCTION_HEADER(ToObjectOrNull)
+    static MToObjectOrNull *New(TempAllocator &alloc, MDefinition *def)
+    {
+        return new(alloc) MToObjectOrNull(def);
+    }
+
+    bool congruentTo(const MDefinition *ins) const {
+        return congruentIfOperandsEqual(ins);
+    }
+
+    AliasSet getAliasSet() const {
+        return AliasSet::None();
+    }
+
+    ALLOW_CLONE(MToObjectOrNull)
+};
+
 class MBitNot
   : public MUnaryInstruction,
     public BitwisePolicy::Data
 {
   protected:
     explicit MBitNot(MDefinition *input)
       : MUnaryInstruction(input)
     {
@@ -7961,16 +7993,92 @@ class MStoreElementHole
         // StoreElementHole can update the initialized length, the array length
         // or reallocate obj->elements.
         return AliasSet::Store(AliasSet::Element | AliasSet::ObjectFields);
     }
 
     ALLOW_CLONE(MStoreElementHole)
 };
 
+// Store an unboxed object or null pointer to a vector.
+class MStoreUnboxedObjectOrNull
+  : public MAryInstruction<3>,
+    public ConvertToObjectOrNullPolicy<2>::Data
+{
+    MStoreUnboxedObjectOrNull(MDefinition *elements, MDefinition *index, MDefinition *value) {
+        initOperand(0, elements);
+        initOperand(1, index);
+        initOperand(2, value);
+        MOZ_ASSERT(elements->type() == MIRType_Elements);
+        MOZ_ASSERT(index->type() == MIRType_Int32);
+    }
+
+  public:
+    INSTRUCTION_HEADER(StoreUnboxedObjectOrNull)
+
+    static MStoreUnboxedObjectOrNull *New(TempAllocator &alloc,
+                                          MDefinition *elements, MDefinition *index,
+                                          MDefinition *value) {
+        return new(alloc) MStoreUnboxedObjectOrNull(elements, index, value);
+    }
+    MDefinition *elements() const {
+        return getOperand(0);
+    }
+    MDefinition *index() const {
+        return getOperand(1);
+    }
+    MDefinition *value() const {
+        return getOperand(2);
+    }
+    AliasSet getAliasSet() const {
+        // Use AliasSet::Element for reference typed object fields.
+        return AliasSet::Store(AliasSet::Element);
+    }
+
+    ALLOW_CLONE(MStoreUnboxedObjectOrNull)
+};
+
+// Store an unboxed object or null pointer to a vector.
+class MStoreUnboxedString
+  : public MAryInstruction<3>,
+    public ConvertToStringPolicy<2>::Data
+{
+    MStoreUnboxedString(MDefinition *elements, MDefinition *index, MDefinition *value) {
+        initOperand(0, elements);
+        initOperand(1, index);
+        initOperand(2, value);
+        MOZ_ASSERT(elements->type() == MIRType_Elements);
+        MOZ_ASSERT(index->type() == MIRType_Int32);
+    }
+
+  public:
+    INSTRUCTION_HEADER(StoreUnboxedString)
+
+    static MStoreUnboxedString *New(TempAllocator &alloc,
+                                    MDefinition *elements, MDefinition *index,
+                                    MDefinition *value) {
+        return new(alloc) MStoreUnboxedString(elements, index, value);
+    }
+    MDefinition *elements() const {
+        return getOperand(0);
+    }
+    MDefinition *index() const {
+        return getOperand(1);
+    }
+    MDefinition *value() const {
+        return getOperand(2);
+    }
+    AliasSet getAliasSet() const {
+        // Use AliasSet::Element for reference typed object fields.
+        return AliasSet::Store(AliasSet::Element);
+    }
+
+    ALLOW_CLONE(MStoreUnboxedString)
+};
+
 // Array.prototype.pop or Array.prototype.shift on a dense array.
 class MArrayPopShift
   : public MUnaryInstruction,
     public SingleObjectPolicy::Data
 {
   public:
     enum Mode {
         Pop,
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -103,16 +103,17 @@ namespace jit {
     _(GuardObject)                                                          \
     _(GuardString)                                                          \
     _(AssertRange)                                                          \
     _(ToDouble)                                                             \
     _(ToFloat32)                                                            \
     _(ToInt32)                                                              \
     _(TruncateToInt32)                                                      \
     _(ToString)                                                             \
+    _(ToObjectOrNull)                                                       \
     _(NewArray)                                                             \
     _(NewArrayCopyOnWrite)                                                  \
     _(NewObject)                                                            \
     _(NewTypedObject)                                                       \
     _(NewDeclEnvObject)                                                     \
     _(NewCallObject)                                                        \
     _(NewRunOnceCallObject)                                                 \
     _(NewStringObject)                                                      \
@@ -171,16 +172,18 @@ namespace jit {
     _(Not)                                                                  \
     _(BoundsCheck)                                                          \
     _(BoundsCheckLower)                                                     \
     _(InArray)                                                              \
     _(LoadElement)                                                          \
     _(LoadElementHole)                                                      \
     _(StoreElement)                                                         \
     _(StoreElementHole)                                                     \
+    _(StoreUnboxedObjectOrNull)                                             \
+    _(StoreUnboxedString)                                                   \
     _(ArrayPopShift)                                                        \
     _(ArrayPush)                                                            \
     _(ArrayConcat)                                                          \
     _(ArrayJoin)                                                            \
     _(LoadTypedArrayElement)                                                \
     _(LoadTypedArrayElementHole)                                            \
     _(LoadTypedArrayElementStatic)                                          \
     _(StoreTypedArrayElement)                                               \
--- a/js/src/jit/ParallelSafetyAnalysis.cpp
+++ b/js/src/jit/ParallelSafetyAnalysis.cpp
@@ -195,16 +195,17 @@ class ParallelSafetyVisitor : public MDe
     SAFE_OP(Unbox)
     SAFE_OP(GuardObject)
     SAFE_OP(ToDouble)
     SAFE_OP(ToFloat32)
     SAFE_OP(ToInt32)
     SAFE_OP(TruncateToInt32)
     SAFE_OP(MaybeToDoubleElement)
     CUSTOM_OP(ToString)
+    UNSAFE_OP(ToObjectOrNull)
     CUSTOM_OP(NewArray)
     UNSAFE_OP(NewArrayCopyOnWrite)
     UNSAFE_OP(NewTypedObject)
     CUSTOM_OP(NewObject)
     CUSTOM_OP(NewCallObject)
     CUSTOM_OP(NewRunOnceCallObject)
     CUSTOM_OP(NewDerivedTypedObject)
     SAFE_OP(ObjectState)
@@ -255,16 +256,18 @@ class ParallelSafetyVisitor : public MDe
     WRITE_GUARDED_OP(SetInitializedLength, elements)
     SAFE_OP(Not)
     SAFE_OP(BoundsCheck)
     SAFE_OP(BoundsCheckLower)
     SAFE_OP(LoadElement)
     SAFE_OP(LoadElementHole)
     MAYBE_WRITE_GUARDED_OP(StoreElement, elements)
     WRITE_GUARDED_OP(StoreElementHole, elements)
+    UNSAFE_OP(StoreUnboxedObjectOrNull)
+    UNSAFE_OP(StoreUnboxedString)
     UNSAFE_OP(ArrayPopShift)
     UNSAFE_OP(ArrayPush)
     SAFE_OP(LoadTypedArrayElement)
     SAFE_OP(LoadTypedArrayElementHole)
     SAFE_OP(LoadTypedArrayElementStatic)
     MAYBE_WRITE_GUARDED_OP(StoreTypedArrayElement, elements)
     WRITE_GUARDED_OP(StoreTypedArrayElementHole, elements)
     UNSAFE_OP(StoreTypedArrayElementStatic)
--- a/js/src/jit/TypePolicy.cpp
+++ b/js/src/jit/TypePolicy.cpp
@@ -400,16 +400,42 @@ ConvertToStringPolicy<Op>::staticAdjustI
     if (!ToStringPolicy::staticAdjustInputs(alloc, replace))
         return false;
 
     return true;
 }
 
 template bool ConvertToStringPolicy<0>::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins);
 template bool ConvertToStringPolicy<1>::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins);
+template bool ConvertToStringPolicy<2>::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins);
+
+template <unsigned Op>
+bool
+ConvertToObjectOrNullPolicy<Op>::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins)
+{
+    MDefinition *in = ins->getOperand(Op);
+    if (in->type() == MIRType_Object ||
+        in->type() == MIRType_Null ||
+        in->type() == MIRType_ObjectOrNull)
+    {
+        return true;
+    }
+
+    MToObjectOrNull *replace = MToObjectOrNull::New(alloc, in);
+    ins->block()->insertBefore(ins, replace);
+    ins->replaceOperand(Op, replace);
+
+    if (!BoxPolicy<0>::staticAdjustInputs(alloc, replace))
+        return false;
+
+    return true;
+}
+
+template bool ConvertToObjectOrNullPolicy<2>::staticAdjustInputs(TempAllocator &alloc,
+                                                                 MInstruction *ins);
 
 template <unsigned Op>
 bool
 IntPolicy<Op>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def)
 {
     MDefinition *in = def->getOperand(Op);
     if (in->type() == MIRType_Int32)
         return true;
@@ -936,16 +962,18 @@ FilterTypeSetPolicy::adjustInputs(TempAl
     _(ToStringPolicy)                           \
     _(TypeBarrierPolicy)
 
 #define TEMPLATE_TYPE_POLICY_LIST(_)                                    \
     _(BoxExceptPolicy<0, MIRType_String>)                               \
     _(BoxPolicy<0>)                                                     \
     _(ConvertToInt32Policy<0>)                                          \
     _(ConvertToStringPolicy<0>)                                         \
+    _(ConvertToStringPolicy<2>)                                         \
+    _(ConvertToObjectOrNullPolicy<2>)                                   \
     _(DoublePolicy<0>)                                                  \
     _(FloatingPointPolicy<0>)                                           \
     _(IntPolicy<0>)                                                     \
     _(IntPolicy<1>)                                                     \
     _(Mix3Policy<ObjectPolicy<0>, BoxExceptPolicy<1, MIRType_String>, BoxPolicy<2> >) \
     _(Mix3Policy<ObjectPolicy<0>, BoxPolicy<1>, BoxPolicy<2> >)         \
     _(Mix3Policy<ObjectPolicy<0>, BoxPolicy<1>, ObjectPolicy<2> >)      \
     _(Mix3Policy<ObjectPolicy<0>, IntPolicy<1>, BoxPolicy<2> >)         \
--- a/js/src/jit/TypePolicy.h
+++ b/js/src/jit/TypePolicy.h
@@ -143,16 +143,29 @@ class ConvertToStringPolicy : public Typ
   public:
     EMPTY_DATA_;
     static bool staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
     bool adjustInputs(TempAllocator &alloc, MInstruction *def) {
         return staticAdjustInputs(alloc, def);
     }
 };
 
+// Expect an object or null value for operand Op. Else a ToObjectOrNull
+// instruction is inserted.
+template <unsigned Op>
+class ConvertToObjectOrNullPolicy : public TypePolicy
+{
+  public:
+    EMPTY_DATA_;
+    static bool staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
+    bool adjustInputs(TempAllocator &alloc, MInstruction *def) {
+        return staticAdjustInputs(alloc, def);
+    }
+};
+
 // Expect an Int for operand Op. If the input is a Value, it is unboxed.
 template <unsigned Op>
 class IntPolicy : public BoxInputsPolicy
 {
   public:
     EMPTY_DATA_;
     static bool staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
     bool adjustInputs(TempAllocator &alloc, MInstruction *def) {
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -1143,16 +1143,23 @@ AssertValidObjectPtr(JSContext *cx, JSOb
         MOZ_ASSERT(obj->isAligned());
         gc::AllocKind kind = obj->asTenured().getAllocKind();
         MOZ_ASSERT(kind >= js::gc::FINALIZE_OBJECT0 && kind <= js::gc::FINALIZE_OBJECT_LAST);
         MOZ_ASSERT(obj->asTenured().zone() == cx->zone());
     }
 }
 
 void
+AssertValidObjectOrNullPtr(JSContext *cx, JSObject *obj)
+{
+    if (obj)
+        AssertValidObjectPtr(cx, obj);
+}
+
+void
 AssertValidStringPtr(JSContext *cx, JSString *str)
 {
     // We can't closely inspect strings from another runtime.
     if (str->runtimeFromAnyThread() != cx->runtime()) {
         MOZ_ASSERT(str->isPermanentAtom());
         return;
     }
 
@@ -1231,16 +1238,23 @@ MarkValueFromIon(JSRuntime *rt, Value *v
 void
 MarkStringFromIon(JSRuntime *rt, JSString **stringp)
 {
     if (*stringp)
         gc::MarkStringUnbarriered(&rt->gc.marker, stringp, "write barrier");
 }
 
 void
+MarkObjectFromIon(JSRuntime *rt, JSObject **objp)
+{
+    if (*objp)
+        gc::MarkObjectUnbarriered(&rt->gc.marker, objp, "write barrier");
+}
+
+void
 MarkShapeFromIon(JSRuntime *rt, Shape **shapep)
 {
     gc::MarkShapeUnbarriered(&rt->gc.marker, shapep, "write barrier");
 }
 
 void
 MarkTypeObjectFromIon(JSRuntime *rt, types::TypeObject **typep)
 {
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -740,37 +740,41 @@ JSString *RegExpReplace(JSContext *cx, H
 JSString *StringReplace(JSContext *cx, HandleString string, HandleString pattern,
                         HandleString repl);
 
 bool SetDenseElement(JSContext *cx, HandleNativeObject obj, int32_t index, HandleValue value,
                      bool strict);
 
 #ifdef DEBUG
 void AssertValidObjectPtr(JSContext *cx, JSObject *obj);
+void AssertValidObjectOrNullPtr(JSContext *cx, JSObject *obj);
 void AssertValidStringPtr(JSContext *cx, JSString *str);
 void AssertValidSymbolPtr(JSContext *cx, JS::Symbol *sym);
 void AssertValidValue(JSContext *cx, Value *v);
 #endif
 
 JSObject *TypedObjectProto(JSObject *obj);
 
 void MarkValueFromIon(JSRuntime *rt, Value *vp);
 void MarkStringFromIon(JSRuntime *rt, JSString **stringp);
+void MarkObjectFromIon(JSRuntime *rt, JSObject **objp);
 void MarkShapeFromIon(JSRuntime *rt, Shape **shapep);
 void MarkTypeObjectFromIon(JSRuntime *rt, types::TypeObject **typep);
 
 // Helper for generatePreBarrier.
 inline void *
 IonMarkFunction(MIRType type)
 {
     switch (type) {
       case MIRType_Value:
         return JS_FUNC_TO_DATA_PTR(void *, MarkValueFromIon);
       case MIRType_String:
         return JS_FUNC_TO_DATA_PTR(void *, MarkStringFromIon);
+      case MIRType_Object:
+        return JS_FUNC_TO_DATA_PTR(void *, MarkObjectFromIon);
       case MIRType_Shape:
         return JS_FUNC_TO_DATA_PTR(void *, MarkShapeFromIon);
       case MIRType_TypeObject:
         return JS_FUNC_TO_DATA_PTR(void *, MarkTypeObjectFromIon);
       default: MOZ_CRASH();
     }
 }
 
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -2498,36 +2498,48 @@ MacroAssemblerARMCompat::store32(Registe
 
 void
 MacroAssemblerARMCompat::store32_NoSecondScratch(Imm32 src, const Address &address)
 {
     move32(src, ScratchRegister);
     storePtr(ScratchRegister, address);
 }
 
-void
-MacroAssemblerARMCompat::storePtr(ImmWord imm, const Address &address)
+template <typename T>
+void
+MacroAssemblerARMCompat::storePtr(ImmWord imm, T address)
 {
     movePtr(imm, ScratchRegister);
     storePtr(ScratchRegister, address);
 }
 
-void
-MacroAssemblerARMCompat::storePtr(ImmPtr imm, const Address &address)
+template void MacroAssemblerARMCompat::storePtr<Address>(ImmWord imm, Address address);
+template void MacroAssemblerARMCompat::storePtr<BaseIndex>(ImmWord imm, BaseIndex address);
+
+template <typename T>
+void
+MacroAssemblerARMCompat::storePtr(ImmPtr imm, T address)
 {
     storePtr(ImmWord(uintptr_t(imm.value)), address);
 }
 
-void
-MacroAssemblerARMCompat::storePtr(ImmGCPtr imm, const Address &address)
+template void MacroAssemblerARMCompat::storePtr<Address>(ImmPtr imm, Address address);
+template void MacroAssemblerARMCompat::storePtr<BaseIndex>(ImmPtr imm, BaseIndex address);
+
+template <typename T>
+void
+MacroAssemblerARMCompat::storePtr(ImmGCPtr imm, T address)
 {
     movePtr(imm, ScratchRegister);
     storePtr(ScratchRegister, address);
 }
 
+template void MacroAssemblerARMCompat::storePtr<Address>(ImmGCPtr imm, Address address);
+template void MacroAssemblerARMCompat::storePtr<BaseIndex>(ImmGCPtr imm, BaseIndex address);
+
 void
 MacroAssemblerARMCompat::storePtr(Register src, const Address &address)
 {
     ma_str(src, Operand(address));
 }
 
 void
 MacroAssemblerARMCompat::storePtr(Register src, const BaseIndex &address)
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -1391,19 +1391,19 @@ class MacroAssemblerARMCompat : public M
     void store32(Register src, AbsoluteAddress address);
     void store32(Register src, const Address &address);
     void store32(Register src, const BaseIndex &address);
     void store32(Imm32 src, const Address &address);
     void store32(Imm32 src, const BaseIndex &address);
 
     void store32_NoSecondScratch(Imm32 src, const Address &address);
 
-    void storePtr(ImmWord imm, const Address &address);
-    void storePtr(ImmPtr imm, const Address &address);
-    void storePtr(ImmGCPtr imm, const Address &address);
+    template <typename T> void storePtr(ImmWord imm, T address);
+    template <typename T> void storePtr(ImmPtr imm, T address);
+    template <typename T> void storePtr(ImmGCPtr imm, T address);
     void storePtr(Register src, const Address &address);
     void storePtr(Register src, const BaseIndex &address);
     void storePtr(Register src, AbsoluteAddress dest);
     void storeDouble(FloatRegister src, Address addr) {
         ma_vstr(src, Operand(addr));
     }
     void storeDouble(FloatRegister src, BaseIndex addr) {
         // Harder cases not handled yet.
--- a/js/src/jit/mips/MacroAssembler-mips.cpp
+++ b/js/src/jit/mips/MacroAssembler-mips.cpp
@@ -2038,36 +2038,48 @@ MacroAssemblerMIPSCompat::store32(Imm32 
 }
 
 void
 MacroAssemblerMIPSCompat::store32(Register src, const BaseIndex &dest)
 {
     ma_store(src, dest, SizeWord);
 }
 
-void
-MacroAssemblerMIPSCompat::storePtr(ImmWord imm, const Address &address)
+template <typename T>
+void
+MacroAssemblerMIPSCompat::storePtr(ImmWord imm, T address)
 {
     ma_li(ScratchRegister, Imm32(imm.value));
     ma_sw(ScratchRegister, address);
 }
 
-void
-MacroAssemblerMIPSCompat::storePtr(ImmPtr imm, const Address &address)
+template void MacroAssemblerMIPSCompat::storePtr<Address>(ImmWord imm, Address address);
+template void MacroAssemblerMIPSCompat::storePtr<BaseIndex>(ImmWord imm, BaseIndex address);
+
+template <typename T>
+void
+MacroAssemblerMIPSCompat::storePtr(ImmPtr imm, T address)
 {
     storePtr(ImmWord(uintptr_t(imm.value)), address);
 }
 
-void
-MacroAssemblerMIPSCompat::storePtr(ImmGCPtr imm, const Address &address)
+template void MacroAssemblerMIPSCompat::storePtr<Address>(ImmPtr imm, Address address);
+template void MacroAssemblerMIPSCompat::storePtr<BaseIndex>(ImmPtr imm, BaseIndex address);
+
+template <typename T>
+void
+MacroAssemblerMIPSCompat::storePtr(ImmGCPtr imm, T address)
 {
     ma_li(ScratchRegister, imm);
     ma_sw(ScratchRegister, address);
 }
 
+template void MacroAssemblerMIPSCompat::storePtr<Address>(ImmGCPtr imm, Address address);
+template void MacroAssemblerMIPSCompat::storePtr<BaseIndex>(ImmGCPtr imm, BaseIndex address);
+
 void
 MacroAssemblerMIPSCompat::storePtr(Register src, const Address &address)
 {
     ma_sw(src, address);
 }
 
 void
 MacroAssemblerMIPSCompat::storePtr(Register src, const BaseIndex &address)
--- a/js/src/jit/mips/MacroAssembler-mips.h
+++ b/js/src/jit/mips/MacroAssembler-mips.h
@@ -1258,19 +1258,19 @@ public:
     void store32(Imm32 src, const BaseIndex &address);
 
     // NOTE: This will use second scratch on MIPS. Only ARM needs the
     // implementation without second scratch.
     void store32_NoSecondScratch(Imm32 src, const Address &address) {
         store32(src, address);
     }
 
-    void storePtr(ImmWord imm, const Address &address);
-    void storePtr(ImmPtr imm, const Address &address);
-    void storePtr(ImmGCPtr imm, const Address &address);
+    template <typename T> void storePtr(ImmWord imm, T address);
+    template <typename T> void storePtr(ImmPtr imm, T address);
+    template <typename T> void storePtr(ImmGCPtr imm, T address);
     void storePtr(Register src, const Address &address);
     void storePtr(Register src, const BaseIndex &address);
     void storePtr(Register src, AbsoluteAddress dest);
     void storeDouble(FloatRegister src, Address addr) {
         ma_sd(src, addr);
     }
     void storeDouble(FloatRegister src, BaseIndex addr) {
         MOZ_ASSERT(addr.offset == 0);
--- a/js/src/jit/x64/MacroAssembler-x64.h
+++ b/js/src/jit/x64/MacroAssembler-x64.h
@@ -761,28 +761,31 @@ class MacroAssemblerX64 : public MacroAs
     void load32(AbsoluteAddress address, Register dest) {
         if (X86Assembler::isAddressImmediate(address.addr)) {
             movl(Operand(address), dest);
         } else {
             mov(ImmPtr(address.addr), ScratchReg);
             load32(Address(ScratchReg, 0x0), dest);
         }
     }
-    void storePtr(ImmWord imm, const Address &address) {
+    template <typename T>
+    void storePtr(ImmWord imm, T address) {
         if ((intptr_t)imm.value <= INT32_MAX && (intptr_t)imm.value >= INT32_MIN) {
             movq(Imm32((int32_t)imm.value), Operand(address));
         } else {
             mov(imm, ScratchReg);
             movq(ScratchReg, Operand(address));
         }
     }
-    void storePtr(ImmPtr imm, const Address &address) {
+    template <typename T>
+    void storePtr(ImmPtr imm, T address) {
         storePtr(ImmWord(uintptr_t(imm.value)), address);
     }
-    void storePtr(ImmGCPtr imm, const Address &address) {
+    template <typename T>
+    void storePtr(ImmGCPtr imm, T address) {
         movq(imm, ScratchReg);
         movq(ScratchReg, Operand(address));
     }
     void storePtr(Register src, const Address &address) {
         movq(src, Operand(address));
     }
     void storePtr(Register src, const BaseIndex &address) {
         movq(src, Operand(address));
--- a/js/src/jit/x86/MacroAssembler-x86.h
+++ b/js/src/jit/x86/MacroAssembler-x86.h
@@ -720,23 +720,26 @@ class MacroAssemblerX86 : public MacroAs
         movl(Operand(address), dest);
     }
     void loadPrivate(const Address &src, Register dest) {
         movl(payloadOf(src), dest);
     }
     void load32(AbsoluteAddress address, Register dest) {
         movl(Operand(address), dest);
     }
-    void storePtr(ImmWord imm, const Address &address) {
+    template <typename T>
+    void storePtr(ImmWord imm, T address) {
         movl(Imm32(imm.value), Operand(address));
     }
-    void storePtr(ImmPtr imm, const Address &address) {
+    template <typename T>
+    void storePtr(ImmPtr imm, T address) {
         storePtr(ImmWord(uintptr_t(imm.value)), address);
     }
-    void storePtr(ImmGCPtr imm, const Address &address) {
+    template <typename T>
+    void storePtr(ImmGCPtr imm, T address) {
         movl(imm, Operand(address));
     }
     void storePtr(Register src, const Address &address) {
         movl(src, Operand(address));
     }
     void storePtr(Register src, const BaseIndex &address) {
         movl(src, Operand(address));
     }