Bug 1131403 - Optimize uses of ObjectOrNull properties of unboxed objects better, r=jandem.
authorBrian Hackett <bhackett1024@gmail.com>
Sat, 14 Feb 2015 14:55:48 -0700
changeset 229252 ac19a93de1789c743e59a36fbcacf1c54a9dc431
parent 229251 77537f14b7369b8dbbf62d1d993bdb416a8d1660
child 229253 b160bea2b0304674189bf3c305babdad47bf0f4e
push id28282
push usercbook@mozilla.com
push dateMon, 16 Feb 2015 15:06:35 +0000
treeherdermozilla-central@09f4968d5f42 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1131403
milestone38.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 1131403 - Optimize uses of ObjectOrNull properties of unboxed objects better, r=jandem.
js/src/jit-test/tests/ion/bailout-with-object-or-null.js
js/src/jit/CodeGenerator.cpp
js/src/jit/CodeGenerator.h
js/src/jit/IonAnalysis.cpp
js/src/jit/JitFrames.cpp
js/src/jit/LIR-Common.h
js/src/jit/LOpcodes.h
js/src/jit/Lowering.cpp
js/src/jit/MIR.h
js/src/jit/MacroAssembler.h
js/src/jit/arm/CodeGenerator-arm.h
js/src/jit/arm/Lowering-arm.cpp
js/src/jit/mips/CodeGenerator-mips.h
js/src/jit/mips/Lowering-mips.cpp
js/src/jit/none/CodeGenerator-none.h
js/src/jit/shared/CodeGenerator-shared.cpp
js/src/jit/shared/CodeGenerator-x86-shared.h
js/src/jit/x64/Lowering-x64.cpp
js/src/jit/x86/Lowering-x86.cpp
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bailout-with-object-or-null.js
@@ -0,0 +1,17 @@
+
+function foo(p) {
+    this.f = p;
+}
+function use(v, a, b) {
+    var f = v.f;
+    g = f;
+    g = a + b;
+    if (f != null)
+        return;
+}
+
+with({}){}
+
+for (var i = 0; i < 2000; i++)
+    use(new foo(i % 2 ? {} : null), 1, 2);
+use(new foo(null), 2147483548, 1000);
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -2204,23 +2204,27 @@ CodeGenerator::visitStoreSlotT(LStoreSlo
     Register base = ToRegister(lir->slots());
     int32_t offset = lir->mir()->slot() * sizeof(js::Value);
     Address dest(base, offset);
 
     if (lir->mir()->needsBarrier())
         emitPreBarrier(dest);
 
     MIRType valueType = lir->mir()->value()->type();
-    ConstantOrRegister value;
-    if (lir->value()->isConstant())
-        value = ConstantOrRegister(*lir->value()->toConstant());
-    else
-        value = TypedOrValueRegister(valueType, ToAnyRegister(lir->value()));
-
-    masm.storeUnboxedValue(value, valueType, dest, lir->mir()->slotType());
+
+    if (valueType == MIRType_ObjectOrNull) {
+        masm.storeObjectOrNull(ToRegister(lir->value()), dest);
+    } else {
+        ConstantOrRegister value;
+        if (lir->value()->isConstant())
+            value = ConstantOrRegister(*lir->value()->toConstant());
+        else
+            value = TypedOrValueRegister(valueType, ToAnyRegister(lir->value()));
+        masm.storeUnboxedValue(value, valueType, dest, lir->mir()->slotType());
+    }
 }
 
 void
 CodeGenerator::visitStoreSlotV(LStoreSlotV *lir)
 {
     Register base = ToRegister(lir->slots());
     int32_t offset = lir->mir()->slot() * sizeof(Value);
 
@@ -3726,17 +3730,17 @@ CodeGenerator::emitObjectOrStringResultC
 
     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);
+            masm.branchPtr(Assembler::Equal, 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);
 
@@ -5330,24 +5334,24 @@ CodeGenerator::visitCompareVM(LCompareVM
         break;
 
       default:
         MOZ_CRASH("Unexpected compare op");
     }
 }
 
 void
-CodeGenerator::visitIsNullOrLikeUndefined(LIsNullOrLikeUndefined *lir)
+CodeGenerator::visitIsNullOrLikeUndefinedV(LIsNullOrLikeUndefinedV *lir)
 {
     JSOp op = lir->mir()->jsop();
     MCompare::CompareType compareType = lir->mir()->compareType();
     MOZ_ASSERT(compareType == MCompare::Compare_Undefined ||
                compareType == MCompare::Compare_Null);
 
-    const ValueOperand value = ToValue(lir, LIsNullOrLikeUndefined::Value);
+    const ValueOperand value = ToValue(lir, LIsNullOrLikeUndefinedV::Value);
     Register output = ToRegister(lir->output());
 
     if (op == JSOP_EQ || op == JSOP_NE) {
         MOZ_ASSERT(lir->mir()->lhs()->type() != MIRType_Object ||
                    lir->mir()->operandMightEmulateUndefined(),
                    "Operands which can't emulate undefined should have been folded");
 
         OutOfLineTestObjectWithLabels *ool = nullptr;
@@ -5404,24 +5408,24 @@ CodeGenerator::visitIsNullOrLikeUndefine
     Assembler::Condition cond = JSOpToCondition(compareType, op);
     if (compareType == MCompare::Compare_Null)
         masm.testNullSet(cond, value, output);
     else
         masm.testUndefinedSet(cond, value, output);
 }
 
 void
-CodeGenerator::visitIsNullOrLikeUndefinedAndBranch(LIsNullOrLikeUndefinedAndBranch *lir)
+CodeGenerator::visitIsNullOrLikeUndefinedAndBranchV(LIsNullOrLikeUndefinedAndBranchV *lir)
 {
     JSOp op = lir->cmpMir()->jsop();
     MCompare::CompareType compareType = lir->cmpMir()->compareType();
     MOZ_ASSERT(compareType == MCompare::Compare_Undefined ||
                compareType == MCompare::Compare_Null);
 
-    const ValueOperand value = ToValue(lir, LIsNullOrLikeUndefinedAndBranch::Value);
+    const ValueOperand value = ToValue(lir, LIsNullOrLikeUndefinedAndBranchV::Value);
 
     if (op == JSOP_EQ || op == JSOP_NE) {
         MBasicBlock *ifTrue;
         MBasicBlock *ifFalse;
 
         if (op == JSOP_EQ) {
             ifTrue = lir->ifTrue();
             ifFalse = lir->ifFalse();
@@ -5471,86 +5475,120 @@ CodeGenerator::visitIsNullOrLikeUndefine
     Assembler::Condition cond = JSOpToCondition(compareType, op);
     if (compareType == MCompare::Compare_Null)
         testNullEmitBranch(cond, value, lir->ifTrue(), lir->ifFalse());
     else
         testUndefinedEmitBranch(cond, value, lir->ifTrue(), lir->ifFalse());
 }
 
 void
-CodeGenerator::visitEmulatesUndefined(LEmulatesUndefined *lir)
+CodeGenerator::visitIsNullOrLikeUndefinedT(LIsNullOrLikeUndefinedT * lir)
 {
     MOZ_ASSERT(lir->mir()->compareType() == MCompare::Compare_Undefined ||
                lir->mir()->compareType() == MCompare::Compare_Null);
-    MOZ_ASSERT(lir->mir()->lhs()->type() == MIRType_Object);
-    MOZ_ASSERT(lir->mir()->operandMightEmulateUndefined(),
-               "If the object couldn't emulate undefined, this should have been folded.");
+
+    MIRType lhsType = lir->mir()->lhs()->type();
+    MOZ_ASSERT(lhsType == MIRType_Object || lhsType == MIRType_ObjectOrNull);
 
     JSOp op = lir->mir()->jsop();
-    MOZ_ASSERT(op == JSOP_EQ || op == JSOP_NE, "Strict equality should have been folded");
-
-    OutOfLineTestObjectWithLabels *ool = new(alloc()) OutOfLineTestObjectWithLabels();
-    addOutOfLineCode(ool, lir->mir());
-
-    Label *emulatesUndefined = ool->label1();
-    Label *doesntEmulateUndefined = ool->label2();
+    MOZ_ASSERT(lhsType == MIRType_ObjectOrNull || op == JSOP_EQ || op == JSOP_NE,
+               "Strict equality should have been folded");
+
+    MOZ_ASSERT(lhsType == MIRType_ObjectOrNull || lir->mir()->operandMightEmulateUndefined(),
+               "If the object couldn't emulate undefined, this should have been folded.");
 
     Register objreg = ToRegister(lir->input());
     Register output = ToRegister(lir->output());
-    branchTestObjectEmulatesUndefined(objreg, emulatesUndefined, doesntEmulateUndefined,
-                                      output, ool);
-
-    Label done;
-
-    masm.move32(Imm32(op == JSOP_NE), output);
-    masm.jump(&done);
-
-    masm.bind(emulatesUndefined);
-    masm.move32(Imm32(op == JSOP_EQ), output);
-    masm.bind(&done);
-}
-
-void
-CodeGenerator::visitEmulatesUndefinedAndBranch(LEmulatesUndefinedAndBranch *lir)
-{
-    MOZ_ASSERT(lir->cmpMir()->compareType() == MCompare::Compare_Undefined ||
-               lir->cmpMir()->compareType() == MCompare::Compare_Null);
-    MOZ_ASSERT(lir->cmpMir()->operandMightEmulateUndefined(),
-               "Operands which can't emulate undefined should have been folded");
+
+    if ((op == JSOP_EQ || op == JSOP_NE) && lir->mir()->operandMightEmulateUndefined()) {
+        OutOfLineTestObjectWithLabels *ool = new(alloc()) OutOfLineTestObjectWithLabels();
+        addOutOfLineCode(ool, lir->mir());
+
+        Label *emulatesUndefined = ool->label1();
+        Label *doesntEmulateUndefined = ool->label2();
+
+        if (lhsType == MIRType_ObjectOrNull)
+            masm.branchTestPtr(Assembler::Zero, objreg, objreg, emulatesUndefined);
+
+        branchTestObjectEmulatesUndefined(objreg, emulatesUndefined, doesntEmulateUndefined,
+                                          output, ool);
+
+        Label done;
+
+        masm.move32(Imm32(op == JSOP_NE), output);
+        masm.jump(&done);
+
+        masm.bind(emulatesUndefined);
+        masm.move32(Imm32(op == JSOP_EQ), output);
+        masm.bind(&done);
+    } else {
+        MOZ_ASSERT(lhsType == MIRType_ObjectOrNull);
+
+        Label isNull, done;
+
+        masm.branchTestPtr(Assembler::Zero, objreg, objreg, &isNull);
+
+        masm.move32(Imm32(op == JSOP_NE || op == JSOP_STRICTNE), output);
+        masm.jump(&done);
+
+        masm.bind(&isNull);
+        masm.move32(Imm32(op == JSOP_EQ || op == JSOP_STRICTEQ), output);
+
+        masm.bind(&done);
+    }
+}
+
+void
+CodeGenerator::visitIsNullOrLikeUndefinedAndBranchT(LIsNullOrLikeUndefinedAndBranchT *lir)
+{
+    DebugOnly<MCompare::CompareType> compareType = lir->cmpMir()->compareType();
+    MOZ_ASSERT(compareType == MCompare::Compare_Undefined ||
+               compareType == MCompare::Compare_Null);
+
+    MIRType lhsType = lir->cmpMir()->lhs()->type();
+    MOZ_ASSERT(lhsType == MIRType_Object || lhsType == MIRType_ObjectOrNull);
 
     JSOp op = lir->cmpMir()->jsop();
-    MOZ_ASSERT(op == JSOP_EQ || op == JSOP_NE, "Strict equality should have been folded");
-
-    OutOfLineTestObject *ool = new(alloc()) OutOfLineTestObject();
-    addOutOfLineCode(ool, lir->cmpMir());
-
-    Label *equal;
-    Label *unequal;
-
-    {
-        MBasicBlock *ifTrue;
-        MBasicBlock *ifFalse;
-
-        if (op == JSOP_EQ) {
-            ifTrue = lir->ifTrue();
-            ifFalse = lir->ifFalse();
-        } else {
-            // Swap branches.
-            ifTrue = lir->ifFalse();
-            ifFalse = lir->ifTrue();
-            op = JSOP_EQ;
-        }
-
-        equal = getJumpLabelForBranch(ifTrue);
-        unequal = getJumpLabelForBranch(ifFalse);
-    }
-
-    Register objreg = ToRegister(lir->input());
-
-    testObjectEmulatesUndefined(objreg, equal, unequal, ToRegister(lir->temp()), ool);
+    MOZ_ASSERT(lhsType == MIRType_ObjectOrNull || op == JSOP_EQ || op == JSOP_NE,
+               "Strict equality should have been folded");
+
+    MOZ_ASSERT(lhsType == MIRType_ObjectOrNull || lir->cmpMir()->operandMightEmulateUndefined(),
+               "If the object couldn't emulate undefined, this should have been folded.");
+
+    MBasicBlock *ifTrue;
+    MBasicBlock *ifFalse;
+
+    if (op == JSOP_EQ || op == JSOP_STRICTEQ) {
+        ifTrue = lir->ifTrue();
+        ifFalse = lir->ifFalse();
+    } else {
+        // Swap branches.
+        ifTrue = lir->ifFalse();
+        ifFalse = lir->ifTrue();
+    }
+
+    Register input = ToRegister(lir->getOperand(0));
+
+    if ((op == JSOP_EQ || op == JSOP_NE) && lir->cmpMir()->operandMightEmulateUndefined()) {
+        OutOfLineTestObject *ool = new(alloc()) OutOfLineTestObject();
+        addOutOfLineCode(ool, lir->cmpMir());
+
+        Label *ifTrueLabel = getJumpLabelForBranch(ifTrue);
+        Label *ifFalseLabel = getJumpLabelForBranch(ifFalse);
+
+        if (lhsType == MIRType_ObjectOrNull)
+            masm.branchTestPtr(Assembler::Zero, input, input, ifTrueLabel);
+
+        // Objects that emulate undefined are loosely equal to null/undefined.
+        Register scratch = ToRegister(lir->temp());
+        testObjectEmulatesUndefined(input, ifTrueLabel, ifFalseLabel, scratch, ool);
+    } else {
+        MOZ_ASSERT(lhsType == MIRType_ObjectOrNull);
+        testZeroEmitBranch(Assembler::Equal, input, ifTrue, ifFalse);
+    }
 }
 
 typedef JSString *(*ConcatStringsFn)(ExclusiveContext *, HandleString, HandleString);
 static const VMFunction ConcatStringsInfo = FunctionInfo<ConcatStringsFn>(ConcatStrings<CanGC>);
 
 void
 CodeGenerator::emitConcat(LInstruction *lir, Register lhs, Register rhs, Register output)
 {
@@ -7700,25 +7738,29 @@ void
 CodeGenerator::visitStoreFixedSlotT(LStoreFixedSlotT *ins)
 {
     const Register obj = ToRegister(ins->getOperand(0));
     size_t slot = ins->mir()->slot();
 
     const LAllocation *value = ins->value();
     MIRType valueType = ins->mir()->value()->type();
 
-    ConstantOrRegister nvalue = value->isConstant()
-                              ? ConstantOrRegister(*value->toConstant())
-                              : TypedOrValueRegister(valueType, ToAnyRegister(value));
-
     Address address(obj, NativeObject::getFixedSlotOffset(slot));
     if (ins->mir()->needsBarrier())
         emitPreBarrier(address);
 
-    masm.storeConstantOrRegister(nvalue, address);
+    if (valueType == MIRType_ObjectOrNull) {
+        Register nvalue = ToRegister(value);
+        masm.storeObjectOrNull(nvalue, address);
+    } else {
+        ConstantOrRegister nvalue = value->isConstant()
+                                    ? ConstantOrRegister(*value->toConstant())
+                                    : TypedOrValueRegister(valueType, ToAnyRegister(value));
+        masm.storeConstantOrRegister(nvalue, address);
+    }
 }
 
 void
 CodeGenerator::visitGetNameCache(LGetNameCache *ins)
 {
     RegisterSet liveRegs = ins->safepoint()->liveRegs();
     Register scopeChain = ToRegister(ins->scopeObj());
     TypedOrValueRegister output(GetValueOutput(ins));
@@ -8466,18 +8508,18 @@ CodeGenerator::visitLoadUnboxedPointerT(
 {
     Register elements = ToRegister(lir->elements());
     const LAllocation *index = lir->index();
     Register out = ToRegister(lir->output());
 
     bool bailOnNull;
     int32_t offsetAdjustment;
     if (lir->mir()->isLoadUnboxedObjectOrNull()) {
-        bailOnNull = lir->mir()->toLoadUnboxedObjectOrNull()->nullBehavior() !=
-                     MLoadUnboxedObjectOrNull::NullNotPossible;
+        bailOnNull = lir->mir()->toLoadUnboxedObjectOrNull()->nullBehavior() ==
+                     MLoadUnboxedObjectOrNull::BailOnNull;
         offsetAdjustment = lir->mir()->toLoadUnboxedObjectOrNull()->offsetAdjustment();
     } else if (lir->mir()->isLoadUnboxedString()) {
         bailOnNull = false;
         offsetAdjustment = lir->mir()->toLoadUnboxedString()->offsetAdjustment();
     } else {
         MOZ_CRASH();
     }
 
@@ -8492,16 +8534,28 @@ CodeGenerator::visitLoadUnboxedPointerT(
     if (bailOnNull) {
         Label bail;
         masm.branchTestPtr(Assembler::Zero, out, out, &bail);
         bailoutFrom(&bail, lir->snapshot());
     }
 }
 
 void
+CodeGenerator::visitUnboxObjectOrNull(LUnboxObjectOrNull *lir)
+{
+    Register obj = ToRegister(lir->input());
+
+    if (lir->mir()->fallible()) {
+        Label bail;
+        masm.branchTestPtr(Assembler::Zero, obj, obj, &bail);
+        bailoutFrom(&bail, lir->snapshot());
+    }
+}
+
+void
 CodeGenerator::visitLoadTypedArrayElement(LLoadTypedArrayElement *lir)
 {
     Register elements = ToRegister(lir->elements());
     Register temp = lir->temp()->isBogusTemp() ? InvalidReg : ToRegister(lir->temp());
     AnyRegister out = ToAnyRegister(lir->output());
 
     Scalar::Type arrayType = lir->mir()->arrayType();
     int width = Scalar::byteSize(arrayType);
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -216,20 +216,20 @@ class CodeGenerator : public CodeGenerat
     void visitMathFunctionF(LMathFunctionF *ins);
     void visitModD(LModD *ins);
     void visitMinMaxI(LMinMaxI *lir);
     void visitBinaryV(LBinaryV *lir);
     void emitCompareS(LInstruction *lir, JSOp op, Register left, Register right, Register output);
     void visitCompareS(LCompareS *lir);
     void visitCompareStrictS(LCompareStrictS *lir);
     void visitCompareVM(LCompareVM *lir);
-    void visitIsNullOrLikeUndefined(LIsNullOrLikeUndefined *lir);
-    void visitIsNullOrLikeUndefinedAndBranch(LIsNullOrLikeUndefinedAndBranch *lir);
-    void visitEmulatesUndefined(LEmulatesUndefined *lir);
-    void visitEmulatesUndefinedAndBranch(LEmulatesUndefinedAndBranch *lir);
+    void visitIsNullOrLikeUndefinedV(LIsNullOrLikeUndefinedV *lir);
+    void visitIsNullOrLikeUndefinedT(LIsNullOrLikeUndefinedT *lir);
+    void visitIsNullOrLikeUndefinedAndBranchV(LIsNullOrLikeUndefinedAndBranchV *lir);
+    void visitIsNullOrLikeUndefinedAndBranchT(LIsNullOrLikeUndefinedAndBranchT *lir);
     void emitConcat(LInstruction *lir, Register lhs, Register rhs, Register output);
     void visitConcat(LConcat *lir);
     void visitCharCodeAt(LCharCodeAt *lir);
     void visitFromCharCode(LFromCharCode *lir);
     void visitStringSplit(LStringSplit *lir);
     void visitFunctionEnvironment(LFunctionEnvironment *lir);
     void visitCallGetProperty(LCallGetProperty *lir);
     void visitCallGetElement(LCallGetElement *lir);
@@ -240,16 +240,17 @@ class CodeGenerator : public CodeGenerat
     void visitOutOfLineTypeOfV(OutOfLineTypeOfV *ool);
     void visitToIdV(LToIdV *lir);
     template<typename T> void emitLoadElementT(LLoadElementT *lir, const T &source);
     void visitLoadElementT(LLoadElementT *lir);
     void visitLoadElementV(LLoadElementV *load);
     void visitLoadElementHole(LLoadElementHole *lir);
     void visitLoadUnboxedPointerV(LLoadUnboxedPointerV *lir);
     void visitLoadUnboxedPointerT(LLoadUnboxedPointerT *lir);
+    void visitUnboxObjectOrNull(LUnboxObjectOrNull *lir);
     void visitStoreElementT(LStoreElementT *lir);
     void visitStoreElementV(LStoreElementV *lir);
     void visitStoreElementHoleT(LStoreElementHoleT *lir);
     void visitStoreElementHoleV(LStoreElementHoleV *lir);
     void visitStoreUnboxedPointer(LStoreUnboxedPointer *lir);
     void emitArrayPopShift(LInstruction *lir, const MArrayPopShift *mir, Register obj,
                            Register elementsTemp, Register lengthTemp, TypedOrValueRegister out);
     void visitArrayPopShiftV(LArrayPopShiftV *lir);
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -2513,16 +2513,61 @@ TryEliminateTypeBarrier(MTypeBarrier *ba
         if (previous == block)
             break;
         block = previous;
     }
 
     return true;
 }
 
+static bool
+TryOptimizeLoadUnboxedObjectOrNull(MLoadUnboxedObjectOrNull *def, MDefinitionVector *peliminateList)
+{
+    if (def->type() != MIRType_Value)
+        return true;
+
+    MDefinitionVector eliminateList(def->block()->graph().alloc());
+
+    for (MUseDefIterator iter(def); iter; ++iter) {
+        MDefinition *ndef = iter.def();
+        switch (ndef->op()) {
+          case MDefinition::Op_Compare:
+            if (ndef->toCompare()->compareType() != MCompare::Compare_Null)
+                return true;
+            break;
+          case MDefinition::Op_PostWriteBarrier:
+            break;
+          case MDefinition::Op_StoreFixedSlot:
+            break;
+          case MDefinition::Op_StoreSlot:
+            break;
+          case MDefinition::Op_ToObjectOrNull:
+            if (!eliminateList.append(ndef->toToObjectOrNull()))
+                return false;
+            break;
+          case MDefinition::Op_Unbox:
+            MOZ_ASSERT(ndef->type() == MIRType_Object);
+            break;
+          default:
+            return true;
+        }
+    }
+
+    def->setResultType(MIRType_ObjectOrNull);
+
+    for (size_t i = 0; i < eliminateList.length(); i++) {
+        MDefinition *ndef = eliminateList[i];
+        ndef->replaceAllUsesWith(def);
+        if (!peliminateList->append(ndef))
+            return false;
+    }
+
+    return true;
+}
+
 static inline MDefinition *
 PassthroughOperand(MDefinition *def)
 {
     if (def->isConvertElementsToDoubles())
         return def->toConvertElementsToDoubles()->elements();
     if (def->isMaybeCopyElementsForWrite())
         return def->toMaybeCopyElementsForWrite()->object();
     return nullptr;
@@ -2560,16 +2605,18 @@ jit::EliminateRedundantChecks(MIRGraph &
     for (MBasicBlockIterator i(graph.begin()); i != graph.end(); i++) {
         MBasicBlock *block = *i;
         if (block->immediateDominator() == block) {
             if (!worklist.append(block))
                 return false;
         }
     }
 
+    MDefinitionVector eliminateList(graph.alloc());
+
     // Starting from each self-dominating block, traverse the CFG in pre-order.
     while (!worklist.empty()) {
         MBasicBlock *block = worklist.popCopy();
 
         // Add all immediate dominators to the front of the worklist.
         if (!worklist.append(block->immediatelyDominatedBlocksBegin(),
                              block->immediatelyDominatedBlocksEnd())) {
             return false;
@@ -2581,31 +2628,40 @@ jit::EliminateRedundantChecks(MIRGraph &
             bool eliminated = false;
 
             if (def->isBoundsCheck()) {
                 if (!TryEliminateBoundsCheck(checks, index, def->toBoundsCheck(), &eliminated))
                     return false;
             } else if (def->isTypeBarrier()) {
                 if (!TryEliminateTypeBarrier(def->toTypeBarrier(), &eliminated))
                     return false;
+            } else if (def->isLoadUnboxedObjectOrNull()) {
+                if (!TryOptimizeLoadUnboxedObjectOrNull(def->toLoadUnboxedObjectOrNull(), &eliminateList))
+                    return false;
             } else {
                 // Now that code motion passes have finished, replace
                 // instructions which pass through one of their operands
                 // (and perform additional checks) with that operand.
                 if (MDefinition *passthrough = PassthroughOperand(def))
                     def->replaceAllUsesWith(passthrough);
             }
 
             if (eliminated)
                 block->discardDef(def);
         }
         index++;
     }
 
     MOZ_ASSERT(index == graph.numBlocks());
+
+    for (size_t i = 0; i < eliminateList.length(); i++) {
+        MDefinition *def = eliminateList[i];
+        def->block()->discardDef(def);
+    }
+
     return true;
 }
 
 bool
 LinearSum::multiply(int32_t scale)
 {
     for (size_t i = 0; i < terms_.length(); i++) {
         if (!SafeMul(scale, terms_[i].scale, &terms_[i].scale))
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -1723,17 +1723,19 @@ uintptr_t
 SnapshotIterator::fromStack(int32_t offset) const
 {
     return ReadFrameSlot(fp_, offset);
 }
 
 static Value
 FromObjectPayload(uintptr_t payload)
 {
-    return ObjectValue(*reinterpret_cast<JSObject *>(payload));
+    // Note: Both MIRType_Object and MIRType_ObjectOrNull are encoded in
+    // snapshots using JSVAL_TYPE_OBJECT.
+    return ObjectOrNullValue(reinterpret_cast<JSObject *>(payload));
 }
 
 static Value
 FromStringPayload(uintptr_t payload)
 {
     return StringValue(reinterpret_cast<JSString *>(payload));
 }
 
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -2493,22 +2493,25 @@ class LBitAndAndBranch : public LControl
     const LAllocation *left() {
         return getOperand(0);
     }
     const LAllocation *right() {
         return getOperand(1);
     }
 };
 
-class LIsNullOrLikeUndefined : public LInstructionHelper<1, BOX_PIECES, 2>
-{
-  public:
-    LIR_HEADER(IsNullOrLikeUndefined)
-
-    LIsNullOrLikeUndefined(const LDefinition &temp, const LDefinition &tempToUnbox)
+// Takes a value and tests whether it is null, undefined, or is an object that
+// emulates |undefined|, as determined by the JSCLASS_EMULATES_UNDEFINED class
+// flag on unwrapped objects.  See also js::EmulatesUndefined.
+class LIsNullOrLikeUndefinedV : public LInstructionHelper<1, BOX_PIECES, 2>
+{
+  public:
+    LIR_HEADER(IsNullOrLikeUndefinedV)
+
+    LIsNullOrLikeUndefinedV(const LDefinition &temp, const LDefinition &tempToUnbox)
     {
         setTemp(0, temp);
         setTemp(1, tempToUnbox);
     }
 
     static const size_t Value = 0;
 
     MCompare *mir() {
@@ -2519,25 +2522,42 @@ class LIsNullOrLikeUndefined : public LI
         return getTemp(0);
     }
 
     const LDefinition *tempToUnbox() {
         return getTemp(1);
     }
 };
 
-class LIsNullOrLikeUndefinedAndBranch : public LControlInstructionHelper<2, BOX_PIECES, 2>
+// Takes an object or object-or-null pointer and tests whether it is null or is
+// an object that emulates |undefined|, as above.
+class LIsNullOrLikeUndefinedT : public LInstructionHelper<1, 1, 0>
+{
+  public:
+    LIR_HEADER(IsNullOrLikeUndefinedT)
+
+    explicit LIsNullOrLikeUndefinedT(const LAllocation &input)
+    {
+        setOperand(0, input);
+    }
+
+    MCompare *mir() {
+        return mir_->toCompare();
+    }
+};
+
+class LIsNullOrLikeUndefinedAndBranchV : public LControlInstructionHelper<2, BOX_PIECES, 2>
 {
     MCompare *cmpMir_;
 
   public:
-    LIR_HEADER(IsNullOrLikeUndefinedAndBranch)
-
-    LIsNullOrLikeUndefinedAndBranch(MCompare *cmpMir, MBasicBlock *ifTrue, MBasicBlock *ifFalse,
-                                    const LDefinition &temp, const LDefinition &tempToUnbox)
+    LIR_HEADER(IsNullOrLikeUndefinedAndBranchV)
+
+    LIsNullOrLikeUndefinedAndBranchV(MCompare *cmpMir, MBasicBlock *ifTrue, MBasicBlock *ifFalse,
+                                     const LDefinition &temp, const LDefinition &tempToUnbox)
       : cmpMir_(cmpMir)
     {
         setSuccessor(0, ifTrue);
         setSuccessor(1, ifFalse);
         setTemp(0, temp);
         setTemp(1, tempToUnbox);
     }
 
@@ -2558,44 +2578,26 @@ class LIsNullOrLikeUndefinedAndBranch : 
     const LDefinition *temp() {
         return getTemp(0);
     }
     const LDefinition *tempToUnbox() {
         return getTemp(1);
     }
 };
 
-// Takes an object and tests whether it emulates |undefined|, as determined by
-// the JSCLASS_EMULATES_UNDEFINED class flag on unwrapped objects.  See also
-// js::EmulatesUndefined.
-class LEmulatesUndefined : public LInstructionHelper<1, 1, 0>
-{
-  public:
-    LIR_HEADER(EmulatesUndefined)
-
-    explicit LEmulatesUndefined(const LAllocation &input)
-    {
-        setOperand(0, input);
-    }
-
-    MCompare *mir() {
-        return mir_->toCompare();
-    }
-};
-
-class LEmulatesUndefinedAndBranch : public LControlInstructionHelper<2, 1, 1>
+class LIsNullOrLikeUndefinedAndBranchT : public LControlInstructionHelper<2, 1, 1>
 {
     MCompare *cmpMir_;
 
   public:
-    LIR_HEADER(EmulatesUndefinedAndBranch)
-
-    LEmulatesUndefinedAndBranch(MCompare *cmpMir, const LAllocation &input,
-                                MBasicBlock *ifTrue, MBasicBlock *ifFalse,
-                                const LDefinition &temp)
+    LIR_HEADER(IsNullOrLikeUndefinedAndBranchT)
+
+    LIsNullOrLikeUndefinedAndBranchT(MCompare *cmpMir, const LAllocation &input,
+                                     MBasicBlock *ifTrue, MBasicBlock *ifFalse,
+                                     const LDefinition &temp)
       : cmpMir_(cmpMir)
     {
         setOperand(0, input);
         setSuccessor(0, ifTrue);
         setSuccessor(1, ifFalse);
         setTemp(0, temp);
     }
 
@@ -4479,16 +4481,34 @@ class LLoadUnboxedPointerT : public LIns
     const LAllocation *elements() {
         return getOperand(0);
     }
     const LAllocation *index() {
         return getOperand(1);
     }
 };
 
+class LUnboxObjectOrNull : public LInstructionHelper<1, 1, 0>
+{
+  public:
+    LIR_HEADER(UnboxObjectOrNull);
+
+    explicit LUnboxObjectOrNull(const LAllocation &input)
+    {
+        setOperand(0, input);
+    }
+
+    MUnbox *mir() const {
+        return mir_->toUnbox();
+    }
+    const LAllocation *input() {
+        return getOperand(0);
+    }
+};
+
 // Store a boxed value to a dense array's element vector.
 class LStoreElementV : public LInstructionHelper<0, 2 + BOX_PIECES, 0>
 {
   public:
     LIR_HEADER(StoreElementV)
 
     LStoreElementV(const LAllocation &elements, const LAllocation &index) {
         setOperand(0, elements);
--- a/js/src/jit/LOpcodes.h
+++ b/js/src/jit/LOpcodes.h
@@ -113,20 +113,20 @@
     _(CompareS)                     \
     _(CompareStrictS)               \
     _(CompareB)                     \
     _(CompareBAndBranch)            \
     _(CompareV)                     \
     _(CompareVAndBranch)            \
     _(CompareVM)                    \
     _(BitAndAndBranch)              \
-    _(IsNullOrLikeUndefined)        \
-    _(IsNullOrLikeUndefinedAndBranch)\
-    _(EmulatesUndefined)            \
-    _(EmulatesUndefinedAndBranch)   \
+    _(IsNullOrLikeUndefinedV)       \
+    _(IsNullOrLikeUndefinedT)       \
+    _(IsNullOrLikeUndefinedAndBranchV)\
+    _(IsNullOrLikeUndefinedAndBranchT)\
     _(MinMaxI)                      \
     _(MinMaxD)                      \
     _(MinMaxF)                      \
     _(NegI)                         \
     _(NegD)                         \
     _(NegF)                         \
     _(AbsI)                         \
     _(AbsD)                         \
@@ -214,16 +214,17 @@
     _(BoundsCheck)                  \
     _(BoundsCheckRange)             \
     _(BoundsCheckLower)             \
     _(LoadElementV)                 \
     _(LoadElementT)                 \
     _(LoadElementHole)              \
     _(LoadUnboxedPointerV)          \
     _(LoadUnboxedPointerT)          \
+    _(UnboxObjectOrNull)            \
     _(StoreElementV)                \
     _(StoreElementT)                \
     _(StoreUnboxedPointer)          \
     _(ArrayPopShiftV)               \
     _(ArrayPopShiftT)               \
     _(ArrayPushV)                   \
     _(ArrayPushT)                   \
     _(ArrayConcat)                  \
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -698,40 +698,43 @@ LIRGenerator::visitTest(MTest *test)
         // Emit LCompare*AndBranch.
 
         // Compare and branch null/undefined.
         // The second operand has known null/undefined type,
         // so just test the first operand.
         if (comp->compareType() == MCompare::Compare_Null ||
             comp->compareType() == MCompare::Compare_Undefined)
         {
-            if (left->type() == MIRType_Object) {
-                MOZ_ASSERT(comp->operandMightEmulateUndefined(),
+            if (left->type() == MIRType_Object || left->type() == MIRType_ObjectOrNull) {
+                MOZ_ASSERT(left->type() == MIRType_ObjectOrNull ||
+                           comp->operandMightEmulateUndefined(),
                            "MCompare::tryFold should handle the never-emulates-undefined case");
 
-                LEmulatesUndefinedAndBranch *lir =
-                    new(alloc()) LEmulatesUndefinedAndBranch(comp, useRegister(left),
-                                                             ifTrue, ifFalse, temp());
+                LDefinition tmp =
+                    comp->operandMightEmulateUndefined() ? temp() : LDefinition::BogusTemp();
+                LIsNullOrLikeUndefinedAndBranchT *lir =
+                    new(alloc()) LIsNullOrLikeUndefinedAndBranchT(comp, useRegister(left),
+                                                                  ifTrue, ifFalse, tmp);
                 add(lir, test);
                 return;
             }
 
             LDefinition tmp, tmpToUnbox;
             if (comp->operandMightEmulateUndefined()) {
                 tmp = temp();
                 tmpToUnbox = tempToUnbox();
             } else {
                 tmp = LDefinition::BogusTemp();
                 tmpToUnbox = LDefinition::BogusTemp();
             }
 
-            LIsNullOrLikeUndefinedAndBranch *lir =
-                new(alloc()) LIsNullOrLikeUndefinedAndBranch(comp, ifTrue, ifFalse,
-                                                             tmp, tmpToUnbox);
-            useBox(lir, LIsNullOrLikeUndefinedAndBranch::Value, left);
+            LIsNullOrLikeUndefinedAndBranchV *lir =
+                new(alloc()) LIsNullOrLikeUndefinedAndBranchV(comp, ifTrue, ifFalse,
+                                                              tmp, tmpToUnbox);
+            useBox(lir, LIsNullOrLikeUndefinedAndBranchV::Value, left);
             add(lir, test);
             return;
         }
 
         // Compare and branch booleans.
         if (comp->compareType() == MCompare::Compare_Boolean) {
             MOZ_ASSERT(left->type() == MIRType_Value);
             MOZ_ASSERT(right->type() == MIRType_Boolean);
@@ -934,35 +937,36 @@ LIRGenerator::visitCompare(MCompare *com
         emitAtUses(comp);
         return;
     }
 
     // Compare Null and Undefined.
     if (comp->compareType() == MCompare::Compare_Null ||
         comp->compareType() == MCompare::Compare_Undefined)
     {
-        if (left->type() == MIRType_Object) {
-            MOZ_ASSERT(comp->operandMightEmulateUndefined(),
+        if (left->type() == MIRType_Object || left->type() == MIRType_ObjectOrNull) {
+            MOZ_ASSERT(left->type() == MIRType_ObjectOrNull ||
+                       comp->operandMightEmulateUndefined(),
                        "MCompare::tryFold should have folded this away");
 
-            define(new(alloc()) LEmulatesUndefined(useRegister(left)), comp);
+            define(new(alloc()) LIsNullOrLikeUndefinedT(useRegister(left)), comp);
             return;
         }
 
         LDefinition tmp, tmpToUnbox;
         if (comp->operandMightEmulateUndefined()) {
             tmp = temp();
             tmpToUnbox = tempToUnbox();
         } else {
             tmp = LDefinition::BogusTemp();
             tmpToUnbox = LDefinition::BogusTemp();
         }
 
-        LIsNullOrLikeUndefined *lir = new(alloc()) LIsNullOrLikeUndefined(tmp, tmpToUnbox);
-        useBox(lir, LIsNullOrLikeUndefined::Value, left);
+        LIsNullOrLikeUndefinedV *lir = new(alloc()) LIsNullOrLikeUndefinedV(tmp, tmpToUnbox);
+        useBox(lir, LIsNullOrLikeUndefinedV::Value, left);
         define(lir, comp);
         return;
     }
 
     // Compare booleans.
     if (comp->compareType() == MCompare::Compare_Boolean) {
         MOZ_ASSERT(left->type() == MIRType_Value);
         MOZ_ASSERT(right->type() == MIRType_Boolean);
@@ -2622,17 +2626,17 @@ LIRGenerator::visitLoadElementHole(MLoad
 }
 
 void
 LIRGenerator::visitLoadUnboxedObjectOrNull(MLoadUnboxedObjectOrNull *ins)
 {
     MOZ_ASSERT(IsValidElementsType(ins->elements(), ins->offsetAdjustment()));
     MOZ_ASSERT(ins->index()->type() == MIRType_Int32);
 
-    if (ins->type() == MIRType_Object) {
+    if (ins->type() == MIRType_Object || ins->type() == MIRType_ObjectOrNull) {
         LLoadUnboxedPointerT *lir = new(alloc()) LLoadUnboxedPointerT(useRegister(ins->elements()),
                                                                       useRegisterOrConstant(ins->index()));
         if (ins->nullBehavior() == MLoadUnboxedObjectOrNull::BailOnNull)
             assignSnapshot(lir, Bailout_TypeBarrierO);
         define(lir, ins);
     } else {
         MOZ_ASSERT(ins->type() == MIRType_Value);
         MOZ_ASSERT(ins->nullBehavior() != MLoadUnboxedObjectOrNull::BailOnNull);
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -611,24 +611,28 @@ class MDefinition : public MNode
 
     TemporaryTypeSet *resultTypeSet() const {
         return resultTypeSet_;
     }
     bool emptyResultTypeSet() const;
 
     bool mightBeType(MIRType type) const {
         MOZ_ASSERT(type != MIRType_Value);
+        MOZ_ASSERT(type != MIRType_ObjectOrNull);
 
         if (type == this->type())
             return true;
 
-        if (MIRType_Value != this->type())
-            return false;
-
-        return !resultTypeSet() || resultTypeSet()->mightBeMIRType(type);
+        if (this->type() == MIRType_ObjectOrNull)
+            return type == MIRType_Object || type == MIRType_Null;
+
+        if (this->type() == MIRType_Value)
+            return !resultTypeSet() || resultTypeSet()->mightBeMIRType(type);
+
+        return false;
     }
 
     bool mightBeMagicType() const;
 
     // Float32 specialization operations (see big comment in IonAnalysis before the Float32
     // specialization algorithm).
     virtual bool isFloat32Commutative() const { return false; }
     virtual bool canProduceFloat32() const { return false; }
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -420,16 +420,27 @@ class MacroAssembler : public MacroAssem
             }
             storeDouble(reg, dest);
         } else {
             storeValue(ValueTypeFromMIRType(src.type()), src.typedReg().gpr(), dest);
         }
     }
 
     template <typename T>
+    void storeObjectOrNull(Register src, const T &dest) {
+        Label notNull, done;
+        branchTestPtr(Assembler::NonZero, src, src, &notNull);
+        storeValue(NullValue(), dest);
+        jump(&done);
+        bind(&notNull);
+        storeValue(JSVAL_TYPE_OBJECT, src, dest);
+        bind(&done);
+    }
+
+    template <typename T>
     void storeConstantOrRegister(ConstantOrRegister src, const T &dest) {
         if (src.constant())
             storeValue(src.value(), dest);
         else
             storeTypedOrValue(src.reg(), dest);
     }
 
     void storeCallResult(Register reg) {
--- a/js/src/jit/arm/CodeGenerator-arm.h
+++ b/js/src/jit/arm/CodeGenerator-arm.h
@@ -97,16 +97,23 @@ class CodeGeneratorARM : public CodeGene
         emitBranch(cond, ifTrue, ifFalse);
     }
     void testObjectEmitBranch(Assembler::Condition cond, const ValueOperand &value,
                               MBasicBlock *ifTrue, MBasicBlock *ifFalse)
     {
         cond = masm.testObject(cond, value);
         emitBranch(cond, ifTrue, ifFalse);
     }
+    void testZeroEmitBranch(Assembler::Condition cond, Register reg,
+                            MBasicBlock *ifTrue, MBasicBlock *ifFalse)
+    {
+        MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
+        masm.cmpPtr(reg, ImmWord(0));
+        emitBranch(cond, ifTrue, ifFalse);
+    }
 
     void emitTableSwitchDispatch(MTableSwitch *mir, Register index, Register base);
 
   public:
     // Instruction visitors.
     virtual void visitMinMaxD(LMinMaxD *ins);
     virtual void visitMinMaxF(LMinMaxF *ins);
     virtual void visitAbsD(LAbsD *ins);
--- a/js/src/jit/arm/Lowering-arm.cpp
+++ b/js/src/jit/arm/Lowering-arm.cpp
@@ -120,20 +120,29 @@ LIRGeneratorARM::visitBox(MBox *box)
     lir->setDef(1, LDefinition::BogusTemp());
     box->setVirtualRegister(vreg);
     add(lir);
 }
 
 void
 LIRGeneratorARM::visitUnbox(MUnbox *unbox)
 {
+    MDefinition *inner = unbox->getOperand(0);
+
+    if (inner->type() == MIRType_ObjectOrNull) {
+        LUnboxObjectOrNull *lir = new(alloc()) LUnboxObjectOrNull(useRegisterAtStart(inner));
+        if (unbox->fallible())
+            assignSnapshot(lir, unbox->bailoutKind());
+        defineReuseInput(lir, unbox, 0);
+        return;
+    }
+
     // An unbox on arm reads in a type tag (either in memory or a register) and
-    // a payload. Unlike most instructions conusming a box, we ask for the type
+    // a payload. Unlike most instructions consuming a box, we ask for the type
     // second, so that the result can re-use the first input.
-    MDefinition *inner = unbox->getOperand(0);
     MOZ_ASSERT(inner->type() == MIRType_Value);
 
     ensureDefined(inner);
 
     if (IsFloatingPointType(unbox->type())) {
         LUnboxFloatingPoint *lir = new(alloc()) LUnboxFloatingPoint(unbox->type());
         if (unbox->fallible())
             assignSnapshot(lir, unbox->bailoutKind());
--- a/js/src/jit/mips/CodeGenerator-mips.h
+++ b/js/src/jit/mips/CodeGenerator-mips.h
@@ -155,16 +155,21 @@ class CodeGeneratorMIPS : public CodeGen
     {
         emitBranch(value.typeReg(), (Imm32)ImmType(JSVAL_TYPE_UNDEFINED), cond, ifTrue, ifFalse);
     }
     void testObjectEmitBranch(Assembler::Condition cond, const ValueOperand &value,
                               MBasicBlock *ifTrue, MBasicBlock *ifFalse)
     {
         emitBranch(value.typeReg(), (Imm32)ImmType(JSVAL_TYPE_OBJECT), cond, ifTrue, ifFalse);
     }
+    void testZeroEmitBranch(Assembler::Condition cond, Register reg,
+                            MBasicBlock *ifTrue, MBasicBlock *ifFalse)
+    {
+        emitBranch(reg, Imm32(0), cond, ifTrue, ifFalse);
+    }
 
     void emitTableSwitchDispatch(MTableSwitch *mir, Register index, Register base);
 
   public:
     // Instruction visitors.
     virtual void visitMinMaxD(LMinMaxD *ins);
     virtual void visitMinMaxF(LMinMaxF *ins);
     virtual void visitAbsD(LAbsD *ins);
--- a/js/src/jit/mips/Lowering-mips.cpp
+++ b/js/src/jit/mips/Lowering-mips.cpp
@@ -122,20 +122,29 @@ LIRGeneratorMIPS::visitBox(MBox *box)
     lir->setDef(1, LDefinition::BogusTemp());
     box->setVirtualRegister(vreg);
     add(lir);
 }
 
 void
 LIRGeneratorMIPS::visitUnbox(MUnbox *unbox)
 {
+    MDefinition *inner = unbox->getOperand(0);
+
+    if (inner->type() == MIRType_ObjectOrNull) {
+        LUnboxObjectOrNull *lir = new(alloc()) LUnboxObjectOrNull(useRegisterAtStart(inner));
+        if (unbox->fallible())
+            assignSnapshot(lir, unbox->bailoutKind());
+        defineReuseInput(lir, unbox, 0);
+        return;
+    }
+
     // An unbox on mips reads in a type tag (either in memory or a register) and
     // a payload. Unlike most instructions consuming a box, we ask for the type
     // second, so that the result can re-use the first input.
-    MDefinition *inner = unbox->getOperand(0);
     MOZ_ASSERT(inner->type() == MIRType_Value);
 
     ensureDefined(inner);
 
     if (IsFloatingPointType(unbox->type())) {
         LUnboxFloatingPoint *lir = new(alloc()) LUnboxFloatingPoint(unbox->type());
         if (unbox->fallible())
             assignSnapshot(lir, unbox->bailoutKind());
--- a/js/src/jit/none/CodeGenerator-none.h
+++ b/js/src/jit/none/CodeGenerator-none.h
@@ -43,16 +43,19 @@ class CodeGeneratorNone : public CodeGen
         MOZ_CRASH();
     }
     void testUndefinedEmitBranch(Assembler::Condition, ValueOperand, MBasicBlock *, MBasicBlock *) {
         MOZ_CRASH();
     }
     void testObjectEmitBranch(Assembler::Condition, ValueOperand, MBasicBlock *, MBasicBlock *) {
         MOZ_CRASH();
     }
+    void testZeroEmitBranch(Assembler::Condition, Register, MBasicBlock *, MBasicBlock *) {
+        MOZ_CRASH();
+    }
     void emitTableSwitchDispatch(MTableSwitch *, Register, Register) { MOZ_CRASH(); }
     ValueOperand ToValue(LInstruction *, size_t) { MOZ_CRASH(); }
     ValueOperand ToOutValue(LInstruction *) { MOZ_CRASH(); }
     ValueOperand ToTempValue(LInstruction *, size_t) { MOZ_CRASH(); }
     void generateInvalidateEpilogue() { MOZ_CRASH(); }
 };
 
 typedef CodeGeneratorNone CodeGeneratorSpecific;
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -373,22 +373,24 @@ CodeGeneratorShared::encodeAllocation(LS
         break;
       case MIRType_Null:
         alloc = RValueAllocation::Null();
         break;
       case MIRType_Int32:
       case MIRType_String:
       case MIRType_Symbol:
       case MIRType_Object:
+      case MIRType_ObjectOrNull:
       case MIRType_Boolean:
       case MIRType_Double:
       case MIRType_Float32:
       {
         LAllocation *payload = snapshot->payloadOfSlot(*allocIndex);
-        JSValueType valueType = ValueTypeFromMIRType(type);
+        JSValueType valueType =
+            (type == MIRType_ObjectOrNull) ? JSVAL_TYPE_OBJECT : ValueTypeFromMIRType(type);
         if (payload->isMemory()) {
             if (type == MIRType_Float32)
                 alloc = RValueAllocation::Float32(ToStackIndex(payload));
             else
                 alloc = RValueAllocation::Typed(valueType, ToStackIndex(payload));
         } else if (payload->isGeneralReg()) {
             alloc = RValueAllocation::Typed(valueType, ToRegister(payload));
         } else if (payload->isFloatReg()) {
--- a/js/src/jit/shared/CodeGenerator-x86-shared.h
+++ b/js/src/jit/shared/CodeGenerator-x86-shared.h
@@ -142,16 +142,24 @@ class CodeGeneratorX86Shared : public Co
     }
     void testObjectEmitBranch(Assembler::Condition cond, const ValueOperand &value,
                                  MBasicBlock *ifTrue, MBasicBlock *ifFalse)
     {
         cond = masm.testObject(cond, value);
         emitBranch(cond, ifTrue, ifFalse);
     }
 
+    void testZeroEmitBranch(Assembler::Condition cond, Register reg,
+                            MBasicBlock *ifTrue, MBasicBlock *ifFalse)
+    {
+        MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
+        masm.cmpPtr(reg, ImmWord(0));
+        emitBranch(cond, ifTrue, ifFalse);
+    }
+
     void emitTableSwitchDispatch(MTableSwitch *mir, Register index, Register base);
 
   public:
     CodeGeneratorX86Shared(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm);
 
   public:
     // Instruction visitors.
     virtual void visitDouble(LDouble *ins);
--- a/js/src/jit/x64/Lowering-x64.cpp
+++ b/js/src/jit/x64/Lowering-x64.cpp
@@ -75,16 +75,25 @@ LIRGeneratorX64::visitBox(MBox *box)
         define(ins, box, LDefinition(LDefinition::BOX));
     }
 }
 
 void
 LIRGeneratorX64::visitUnbox(MUnbox *unbox)
 {
     MDefinition *box = unbox->getOperand(0);
+
+    if (box->type() == MIRType_ObjectOrNull) {
+        LUnboxObjectOrNull *lir = new(alloc()) LUnboxObjectOrNull(useRegisterAtStart(box));
+        if (unbox->fallible())
+            assignSnapshot(lir, unbox->bailoutKind());
+        defineReuseInput(lir, unbox, 0);
+        return;
+    }
+
     MOZ_ASSERT(box->type() == MIRType_Value);
 
     LUnboxBase *lir;
     if (IsFloatingPointType(unbox->type())) {
         lir = new(alloc()) LUnboxFloatingPoint(useRegisterAtStart(box), unbox->type());
     } else if (unbox->fallible()) {
         // If the unbox is fallible, load the Value in a register first to
         // avoid multiple loads.
--- a/js/src/jit/x86/Lowering-x86.cpp
+++ b/js/src/jit/x86/Lowering-x86.cpp
@@ -93,20 +93,29 @@ LIRGeneratorX86::visitBox(MBox *box)
     lir->setDef(1, LDefinition::BogusTemp());
     box->setVirtualRegister(vreg);
     add(lir);
 }
 
 void
 LIRGeneratorX86::visitUnbox(MUnbox *unbox)
 {
+    MDefinition *inner = unbox->getOperand(0);
+
+    if (inner->type() == MIRType_ObjectOrNull) {
+        LUnboxObjectOrNull *lir = new(alloc()) LUnboxObjectOrNull(useRegisterAtStart(inner));
+        if (unbox->fallible())
+            assignSnapshot(lir, unbox->bailoutKind());
+        defineReuseInput(lir, unbox, 0);
+        return;
+    }
+
     // An unbox on x86 reads in a type tag (either in memory or a register) and
-    // a payload. Unlike most instructions conusming a box, we ask for the type
+    // a payload. Unlike most instructions consuming a box, we ask for the type
     // second, so that the result can re-use the first input.
-    MDefinition *inner = unbox->getOperand(0);
     MOZ_ASSERT(inner->type() == MIRType_Value);
 
     ensureDefined(inner);
 
     if (IsFloatingPointType(unbox->type())) {
         LUnboxFloatingPoint *lir = new(alloc()) LUnboxFloatingPoint(unbox->type());
         if (unbox->fallible())
             assignSnapshot(lir, unbox->bailoutKind());