Bug 777262: IonMonkey: Inline more compare operations, r=jandem
authorHannes Verschore <hv1989@gmail.com>
Sat, 22 Dec 2012 23:07:59 +0100
changeset 126006 6bc692ff1c10fa3d2f045d3764e701c101c298db
parent 126005 5abe31fa4a5c6847d1c5c6a5a6b6957be0821ea0
child 126007 2e6bec0c1f8a262c3566966332470e1f9dcd589b
push id2151
push userlsblakk@mozilla.com
push dateTue, 19 Feb 2013 18:06:57 +0000
treeherdermozilla-beta@4952e88741ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs777262
milestone20.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 777262: IonMonkey: Inline more compare operations, r=jandem
js/src/ion/CodeGenerator.cpp
js/src/ion/CodeGenerator.h
js/src/ion/IonAnalysis.cpp
js/src/ion/LIR-Common.h
js/src/ion/LOpcodes.h
js/src/ion/Lowering.cpp
js/src/ion/MIR.cpp
js/src/ion/MIR.h
js/src/ion/TypePolicy.cpp
js/src/ion/TypePolicy.h
js/src/ion/arm/CodeGenerator-arm.cpp
js/src/ion/arm/CodeGenerator-arm.h
js/src/ion/shared/CodeGenerator-x86-shared.cpp
js/src/ion/shared/CodeGenerator-x86-shared.h
js/src/ion/x64/CodeGenerator-x64.cpp
js/src/ion/x64/CodeGenerator-x64.h
js/src/ion/x86/CodeGenerator-x86.cpp
js/src/ion/x86/CodeGenerator-x86.h
js/src/jsinfer.h
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -2405,17 +2405,17 @@ static const VMFunction NeInfo = Functio
 static const VMFunction StrictEqInfo = FunctionInfo<CompareFn>(ion::StrictlyEqual<true>);
 static const VMFunction StrictNeInfo = FunctionInfo<CompareFn>(ion::StrictlyEqual<false>);
 static const VMFunction LtInfo = FunctionInfo<CompareFn>(ion::LessThan);
 static const VMFunction LeInfo = FunctionInfo<CompareFn>(ion::LessThanOrEqual);
 static const VMFunction GtInfo = FunctionInfo<CompareFn>(ion::GreaterThan);
 static const VMFunction GeInfo = FunctionInfo<CompareFn>(ion::GreaterThanOrEqual);
 
 bool
-CodeGenerator::visitCompareV(LCompareV *lir)
+CodeGenerator::visitCompareVM(LCompareVM *lir)
 {
     pushArg(ToValue(lir, LBinaryV::RhsInput));
     pushArg(ToValue(lir, LBinaryV::LhsInput));
 
     switch (lir->mir()->jsop()) {
       case JSOP_EQ:
         return callVM(EqInfo, lir);
 
@@ -2445,18 +2445,19 @@ CodeGenerator::visitCompareV(LCompareV *
         return false;
     }
 }
 
 bool
 CodeGenerator::visitIsNullOrLikeUndefined(LIsNullOrLikeUndefined *lir)
 {
     JSOp op = lir->mir()->jsop();
-    MIRType specialization = lir->mir()->specialization();
-    JS_ASSERT(IsNullOrUndefined(specialization));
+    MCompare::CompareType compareType = lir->mir()->compareType();
+    JS_ASSERT(compareType == MCompare::Compare_Undefined ||
+              compareType == MCompare::Compare_Null);
 
     const ValueOperand value = ToValue(lir, LIsNullOrLikeUndefined::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");
@@ -2507,31 +2508,32 @@ CodeGenerator::visitIsNullOrLikeUndefine
         // Both branches meet here.
         masm.bind(&done);
         return true;
     }
 
     JS_ASSERT(op == JSOP_STRICTEQ || op == JSOP_STRICTNE);
 
     Assembler::Condition cond = JSOpToCondition(op);
-    if (specialization == MIRType_Null)
+    if (compareType == MCompare::Compare_Null)
         cond = masm.testNull(cond, value);
     else
         cond = masm.testUndefined(cond, value);
 
     emitSet(cond, output);
     return true;
 }
 
 bool
 CodeGenerator::visitIsNullOrLikeUndefinedAndBranch(LIsNullOrLikeUndefinedAndBranch *lir)
 {
     JSOp op = lir->mir()->jsop();
-    MIRType specialization = lir->mir()->specialization();
-    JS_ASSERT(IsNullOrUndefined(specialization));
+    MCompare::CompareType compareType = lir->mir()->compareType();
+    JS_ASSERT(compareType == MCompare::Compare_Undefined ||
+              compareType == MCompare::Compare_Null);
 
     const ValueOperand value = ToValue(lir, LIsNullOrLikeUndefinedAndBranch::Value);
 
     if (op == JSOP_EQ || op == JSOP_NE) {
         MBasicBlock *ifTrue;
         MBasicBlock *ifFalse;
 
         if (op == JSOP_EQ) {
@@ -2572,32 +2574,33 @@ CodeGenerator::visitIsNullOrLikeUndefine
             masm.jump(ifFalseLabel);
         }
         return true;
     }
 
     JS_ASSERT(op == JSOP_STRICTEQ || op == JSOP_STRICTNE);
 
     Assembler::Condition cond = JSOpToCondition(op);
-    if (specialization == MIRType_Null)
+    if (compareType == MCompare::MCompare::Compare_Null)
         cond = masm.testNull(cond, value);
     else
         cond = masm.testUndefined(cond, value);
 
     emitBranch(cond, lir->ifTrue(), lir->ifFalse());
     return true;
 }
 
 typedef JSString *(*ConcatStringsFn)(JSContext *, HandleString, HandleString);
 static const VMFunction ConcatStringsInfo = FunctionInfo<ConcatStringsFn>(js_ConcatStrings);
 
 bool
 CodeGenerator::visitEmulatesUndefined(LEmulatesUndefined *lir)
 {
-    MOZ_ASSERT(IsNullOrUndefined(lir->mir()->specialization()));
+    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.");
 
     JSOp op = lir->mir()->jsop();
     MOZ_ASSERT(op == JSOP_EQ || op == JSOP_NE, "Strict equality should have been folded");
 
     OutOfLineTestObjectWithLabels *ool = new OutOfLineTestObjectWithLabels();
@@ -2621,17 +2624,18 @@ CodeGenerator::visitEmulatesUndefined(LE
     masm.move32(Imm32(op == JSOP_EQ), output);
     masm.bind(&done);
     return true;
 }
 
 bool
 CodeGenerator::visitEmulatesUndefinedAndBranch(LEmulatesUndefinedAndBranch *lir)
 {
-    MOZ_ASSERT(IsNullOrUndefined(lir->mir()->specialization()));
+    MOZ_ASSERT(lir->mir()->compareType() == MCompare::Compare_Undefined ||
+               lir->mir()->compareType() == MCompare::Compare_Null);
     MOZ_ASSERT(lir->mir()->operandMightEmulateUndefined(),
                "Operands which can't emulate undefined should have been folded");
 
     JSOp op = lir->mir()->jsop();
     MOZ_ASSERT(op == JSOP_EQ || op == JSOP_NE, "Strict equality should have been folded");
 
     OutOfLineTestObject *ool = new OutOfLineTestObject();
     if (!addOutOfLineCode(ool))
--- a/js/src/ion/CodeGenerator.h
+++ b/js/src/ion/CodeGenerator.h
@@ -123,17 +123,17 @@ class CodeGenerator : public CodeGenerat
     bool visitPowD(LPowD *lir);
     bool visitNegD(LNegD *lir);
     bool visitRandom(LRandom *lir);
     bool visitMathFunctionD(LMathFunctionD *ins);
     bool visitModD(LModD *ins);
     bool visitMinMaxI(LMinMaxI *lir);
     bool visitBinaryV(LBinaryV *lir);
     bool visitCompareS(LCompareS *lir);
-    bool visitCompareV(LCompareV *lir);
+    bool visitCompareVM(LCompareVM *lir);
     bool visitIsNullOrLikeUndefined(LIsNullOrLikeUndefined *lir);
     bool visitIsNullOrLikeUndefinedAndBranch(LIsNullOrLikeUndefinedAndBranch *lir);
     bool visitEmulatesUndefined(LEmulatesUndefined *lir);
     bool visitEmulatesUndefinedAndBranch(LEmulatesUndefinedAndBranch *lir);
     bool visitConcat(LConcat *lir);
     bool visitCharCodeAt(LCharCodeAt *lir);
     bool visitFromCharCode(LFromCharCode *lir);
     bool visitFunctionEnvironment(LFunctionEnvironment *lir);
--- a/js/src/ion/IonAnalysis.cpp
+++ b/js/src/ion/IonAnalysis.cpp
@@ -1082,17 +1082,17 @@ ion::ExtractLinearInequality(MTest *test
     if (!test->getOperand(0)->isCompare())
         return false;
 
     MCompare *compare = test->getOperand(0)->toCompare();
 
     MDefinition *lhs = compare->getOperand(0);
     MDefinition *rhs = compare->getOperand(1);
 
-    if (compare->specialization() != MIRType_Int32)
+    if (compare->compareType() != MCompare::Compare_Int32)
         return false;
 
     JS_ASSERT(lhs->type() == MIRType_Int32);
     JS_ASSERT(rhs->type() == MIRType_Int32);
 
     JSOp jsop = compare->jsop();
     if (direction == FALSE_BRANCH)
         jsop = analyze::NegateCompareOp(jsop);
--- a/js/src/ion/LIR-Common.h
+++ b/js/src/ion/LIR-Common.h
@@ -954,17 +954,16 @@ class LPolyInlineDispatch : public LInst
         return getTemp(0);
     }
 
     MPolyInlineDispatch *mir() {
         return mir_->toPolyInlineDispatch();
     }
 };
 
-
 // Compares two integral values of the same JS type, either integer or object.
 // For objects, both operands are in registers.
 class LCompare : public LInstructionHelper<1, 2, 0>
 {
     JSOp jsop_;
 
   public:
     LIR_HEADER(Compare)
@@ -984,74 +983,16 @@ class LCompare : public LInstructionHelp
     const LAllocation *right() {
         return getOperand(1);
     }
     MCompare *mir() {
         return mir_->toCompare();
     }
 };
 
-class LCompareD : public LInstructionHelper<1, 2, 0>
-{
-  public:
-    LIR_HEADER(CompareD)
-    LCompareD(const LAllocation &left, const LAllocation &right) {
-        setOperand(0, left);
-        setOperand(1, right);
-    }
-
-    const LAllocation *left() {
-        return getOperand(0);
-    }
-    const LAllocation *right() {
-        return getOperand(1);
-    }
-    MCompare *mir() {
-        return mir_->toCompare();
-    }
-};
-
-class LCompareS : public LInstructionHelper<1, 2, 1>
-{
-  public:
-    LIR_HEADER(CompareS)
-    LCompareS(const LAllocation &left, const LAllocation &right,
-              const LDefinition &temp) {
-        setOperand(0, left);
-        setOperand(1, right);
-        setTemp(0, temp);
-    }
-
-    const LAllocation *left() {
-        return getOperand(0);
-    }
-    const LAllocation *right() {
-        return getOperand(1);
-    }
-    const LDefinition *temp() {
-        return getTemp(0);
-    }
-    MCompare *mir() {
-        return mir_->toCompare();
-    }
-};
-
-class LCompareV : public LCallInstructionHelper<1, 2 * BOX_PIECES, 0>
-{
-  public:
-    LIR_HEADER(CompareV)
-
-    static const size_t LhsInput = 0;
-    static const size_t RhsInput = BOX_PIECES;
-
-    MCompare *mir() const {
-        return mir_->toCompare();
-    }
-};
-
 // Compares two integral values of the same JS type, either integer or object.
 // For objects, both operands are in registers.
 class LCompareAndBranch : public LInstructionHelper<0, 2, 0>
 {
     JSOp jsop_;
     MBasicBlock *ifTrue_;
     MBasicBlock *ifFalse_;
 
@@ -1082,16 +1023,36 @@ class LCompareAndBranch : public LInstru
     const LAllocation *right() {
         return getOperand(1);
     }
     MCompare *mir() {
         return mir_->toCompare();
     }
 };
 
+class LCompareD : public LInstructionHelper<1, 2, 0>
+{
+  public:
+    LIR_HEADER(CompareD)
+    LCompareD(const LAllocation &left, const LAllocation &right) {
+        setOperand(0, left);
+        setOperand(1, right);
+    }
+
+    const LAllocation *left() {
+        return getOperand(0);
+    }
+    const LAllocation *right() {
+        return getOperand(1);
+    }
+    MCompare *mir() {
+        return mir_->toCompare();
+    }
+};
+
 class LCompareDAndBranch : public LInstructionHelper<0, 2, 0>
 {
     MBasicBlock *ifTrue_;
     MBasicBlock *ifFalse_;
 
   public:
     LIR_HEADER(CompareDAndBranch)
     LCompareDAndBranch(const LAllocation &left, const LAllocation &right,
@@ -1115,16 +1076,41 @@ class LCompareDAndBranch : public LInstr
     const LAllocation *right() {
         return getOperand(1);
     }
     MCompare *mir() {
         return mir_->toCompare();
     }
 };
 
+class LCompareS : public LInstructionHelper<1, 2, 1>
+{
+  public:
+    LIR_HEADER(CompareS)
+    LCompareS(const LAllocation &left, const LAllocation &right,
+              const LDefinition &temp) {
+        setOperand(0, left);
+        setOperand(1, right);
+        setTemp(0, temp);
+    }
+
+    const LAllocation *left() {
+        return getOperand(0);
+    }
+    const LAllocation *right() {
+        return getOperand(1);
+    }
+    const LDefinition *temp() {
+        return getTemp(0);
+    }
+    MCompare *mir() {
+        return mir_->toCompare();
+    }
+};
+
 // Used for strict-equality comparisons where one side is a boolean
 // and the other is a value. Note that CompareI is used to compare
 // two booleans.
 class LCompareB : public LInstructionHelper<1, BOX_PIECES + 1, 0>
 {
   public:
     LIR_HEADER(CompareB)
 
@@ -1169,16 +1155,69 @@ class LCompareBAndBranch : public LInstr
     MBasicBlock *ifFalse() const {
         return ifFalse_;
     }
     MCompare *mir() {
         return mir_->toCompare();
     }
 };
 
+class LCompareV : public LInstructionHelper<1, 2 * BOX_PIECES, 0>
+{
+  public:
+    LIR_HEADER(CompareV)
+
+    static const size_t LhsInput = 0;
+    static const size_t RhsInput = BOX_PIECES;
+
+    MCompare *mir() const {
+        return mir_->toCompare();
+    }
+};
+
+class LCompareVAndBranch : public LInstructionHelper<0, 2 * BOX_PIECES, 0>
+{
+    MBasicBlock *ifTrue_;
+    MBasicBlock *ifFalse_;
+
+  public:
+    LIR_HEADER(CompareVAndBranch)
+
+    static const size_t LhsInput = 0;
+    static const size_t RhsInput = BOX_PIECES;
+
+    LCompareVAndBranch(MBasicBlock *ifTrue, MBasicBlock *ifFalse)
+      : ifTrue_(ifTrue),
+        ifFalse_(ifFalse)
+    { }
+
+    MBasicBlock *ifTrue() const {
+        return ifTrue_;
+    }
+    MBasicBlock *ifFalse() const {
+        return ifFalse_;
+    }
+    MCompare *mir() {
+        return mir_->toCompare();
+    }
+};
+
+class LCompareVM : public LCallInstructionHelper<1, 2 * BOX_PIECES, 0>
+{
+  public:
+    LIR_HEADER(CompareVM)
+
+    static const size_t LhsInput = 0;
+    static const size_t RhsInput = BOX_PIECES;
+
+    MCompare *mir() const {
+        return mir_->toCompare();
+    }
+};
+
 class LIsNullOrLikeUndefined : public LInstructionHelper<1, BOX_PIECES, 2>
 {
   public:
     LIR_HEADER(IsNullOrLikeUndefined)
 
     LIsNullOrLikeUndefined(const LDefinition &temp0, const LDefinition &temp1)
     {
         setTemp(0, temp0);
--- a/js/src/ion/LOpcodes.h
+++ b/js/src/ion/LOpcodes.h
@@ -52,23 +52,25 @@
     _(Throw)                        \
     _(Phi)                          \
     _(TestIAndBranch)               \
     _(TestDAndBranch)               \
     _(TestVAndBranch)               \
     _(TestOAndBranch)               \
     _(PolyInlineDispatch)           \
     _(Compare)                      \
+    _(CompareAndBranch)             \
     _(CompareD)                     \
+    _(CompareDAndBranch)            \
     _(CompareS)                     \
-    _(CompareV)                     \
-    _(CompareAndBranch)             \
-    _(CompareDAndBranch)            \
     _(CompareB)                     \
     _(CompareBAndBranch)            \
+    _(CompareV)                     \
+    _(CompareVAndBranch)            \
+    _(CompareVM)                    \
     _(IsNullOrLikeUndefined)        \
     _(IsNullOrLikeUndefinedAndBranch)\
     _(EmulatesUndefined)            \
     _(EmulatesUndefinedAndBranch)   \
     _(MinMaxI)                      \
     _(MinMaxD)                      \
     _(NegD)                         \
     _(AbsI)                         \
--- a/js/src/ion/Lowering.cpp
+++ b/js/src/ion/Lowering.cpp
@@ -426,31 +426,24 @@ LIRGenerator::visitTest(MTest *test)
         MDefinition *left = comp->lhs();
         MDefinition *right = comp->rhs();
 
         // Try to fold the comparison so that we don't have to handle all cases.
         bool result;
         if (comp->tryFold(&result))
             return add(new LGoto(result ? ifTrue : ifFalse));
 
-        if (comp->specialization() == MIRType_Int32 || comp->specialization() == MIRType_Object) {
-            JSOp op = ReorderComparison(comp->jsop(), &left, &right);
-            LAllocation rhs = comp->specialization() == MIRType_Object
-                              ? useRegister(right)
-                              : useAnyOrConstant(right);
-            return add(new LCompareAndBranch(op, useRegister(left), rhs, ifTrue, ifFalse), comp);
-        }
-        if (comp->specialization() == MIRType_Double) {
-            return add(new LCompareDAndBranch(useRegister(left), useRegister(right), ifTrue,
-                                              ifFalse), comp);
-        }
+        // Emit LCompare*AndBranch.
 
-        // The second operand has known null/undefined type, so just test the
-        // first operand.
-        if (IsNullOrUndefined(comp->specialization())) {
+        // 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(),
                            "MCompare::tryFold should handle the never-emulates-undefined case");
 
                 LEmulatesUndefinedAndBranch *lir =
                     new LEmulatesUndefinedAndBranch(useRegister(left), ifTrue, ifFalse, temp());
                 return add(lir, comp);
             }
@@ -466,26 +459,58 @@ LIRGenerator::visitTest(MTest *test)
 
             LIsNullOrLikeUndefinedAndBranch *lir =
                 new LIsNullOrLikeUndefinedAndBranch(ifTrue, ifFalse, temp0, temp1);
             if (!useBox(lir, LIsNullOrLikeUndefinedAndBranch::Value, left))
                 return false;
             return add(lir, comp);
         }
 
-        if (comp->specialization() == MIRType_Boolean) {
+        // Compare and branch booleans.
+        if (comp->compareType() == MCompare::Compare_Boolean) {
             JS_ASSERT(left->type() == MIRType_Value);
             JS_ASSERT(right->type() == MIRType_Boolean);
 
-            LCompareBAndBranch *lir = new LCompareBAndBranch(useRegisterOrConstant(right),
-                                                             ifTrue, ifFalse);
+            LAllocation rhs = useRegisterOrConstant(right);
+            LCompareBAndBranch *lir = new LCompareBAndBranch(rhs, ifTrue, ifFalse);
             if (!useBox(lir, LCompareBAndBranch::Lhs, left))
                 return false;
             return add(lir, comp);
         }
+
+        // Compare and branch Int32 or Object pointers.
+        if (comp->compareType() == MCompare::Compare_Int32 ||
+            comp->compareType() == MCompare::Compare_Object)
+        {
+            JSOp op = ReorderComparison(comp->jsop(), &left, &right);
+            LAllocation lhs = useRegister(left);
+            LAllocation rhs = useRegister(right);
+            if (comp->compareType() == MCompare::Compare_Int32)
+                rhs = useAnyOrConstant(right);
+            LCompareAndBranch *lir = new LCompareAndBranch(op, lhs, rhs, ifTrue, ifFalse);
+            return add(lir, comp);
+        }
+
+        // Compare and branch doubles.
+        if (comp->compareType() == MCompare::Compare_Double) {
+            LAllocation lhs = useRegister(left);
+            LAllocation rhs = useRegister(right);
+            LCompareDAndBranch *lir = new LCompareDAndBranch(lhs, rhs, ifTrue, ifFalse);
+            return add(lir, comp);
+        }
+
+        // Compare values.
+        if (comp->compareType() == MCompare::Compare_Value) {
+            LCompareVAndBranch *lir = new LCompareVAndBranch(ifTrue, ifFalse);
+            if (!useBoxAtStart(lir, LCompareVAndBranch::LhsInput, left))
+                return false;
+            if (!useBoxAtStart(lir, LCompareVAndBranch::RhsInput, right))
+                return false;
+            return add(lir, comp);
+        }
     }
 
     if (opd->type() == MIRType_Double)
         return add(new LTestDAndBranch(useRegister(opd), ifTrue, ifFalse));
 
     JS_ASSERT(opd->type() == MIRType_Int32 || opd->type() == MIRType_Boolean);
     return add(new LTestIAndBranch(useRegister(opd), ifTrue, ifFalse));
 }
@@ -521,62 +546,52 @@ CanEmitCompareAtUses(MInstruction *ins)
 }
 
 bool
 LIRGenerator::visitCompare(MCompare *comp)
 {
     MDefinition *left = comp->lhs();
     MDefinition *right = comp->rhs();
 
-    if (comp->specialization() != MIRType_None) {
-        // Try to fold the comparison so that we don't have to handle all cases.
-        bool result;
-        if (comp->tryFold(&result))
-            return define(new LInteger(result), comp);
+    // Try to fold the comparison so that we don't have to handle all cases.
+    bool result;
+    if (comp->tryFold(&result))
+        return define(new LInteger(result), comp);
 
-        // Move below the emitAtUses call if we ever implement
-        // LCompareSAndBranch. Doing this now wouldn't be wrong, but doesn't
-        // make sense and avoids confusion.
-        if (comp->specialization() == MIRType_String) {
-            LCompareS *lir = new LCompareS(useRegister(left), useRegister(right), temp());
-            if (!define(lir, comp))
-                return false;
-            return assignSafepoint(lir, comp);
-        }
-
-        // Sniff out if the output of this compare is used only for a branching.
-        // If it is, then we will emit an LCompare*AndBranch instruction in place
-        // of this compare and any test that uses this compare. Thus, we can
-        // ignore this Compare.
-        if (CanEmitCompareAtUses(comp))
-            return emitAtUses(comp);
+    // Move below the emitAtUses call if we ever implement
+    // LCompareSAndBranch. Doing this now wouldn't be wrong, but doesn't
+    // make sense and avoids confusion.
+    if (comp->compareType() == MCompare::Compare_String) {
+        LCompareS *lir = new LCompareS(useRegister(left), useRegister(right), temp());
+        if (!define(lir, comp))
+            return false;
+        return assignSafepoint(lir, comp);
+    }
 
-        if (comp->specialization() == MIRType_Int32 || comp->specialization() == MIRType_Object) {
-            JSOp op = ReorderComparison(comp->jsop(), &left, &right);
-            LAllocation rhs = comp->specialization() == MIRType_Object
-                              ? useRegister(right)
-                              : useAnyOrConstant(right);
-            return define(new LCompare(op, useRegister(left), rhs), comp);
-        }
-
-        if (comp->specialization() == MIRType_Double)
-            return define(new LCompareD(useRegister(left), useRegister(right)), comp);
+    // Unknown/unspecialized compare use a VM call.
+    if (comp->compareType() == MCompare::Compare_Unknown) {
+        LCompareVM *lir = new LCompareVM();
+        if (!useBoxAtStart(lir, LCompareVM::LhsInput, left))
+            return false;
+        if (!useBoxAtStart(lir, LCompareVM::RhsInput, right))
+            return false;
+        return defineReturn(lir, comp) && assignSafepoint(lir, comp);
+    }
 
-        if (comp->specialization() == MIRType_Boolean) {
-            JS_ASSERT(left->type() == MIRType_Value);
-            JS_ASSERT(right->type() == MIRType_Boolean);
+    // Sniff out if the output of this compare is used only for a branching.
+    // If it is, then we will emit an LCompare*AndBranch instruction in place
+    // of this compare and any test that uses this compare. Thus, we can
+    // ignore this Compare.
+    if (CanEmitCompareAtUses(comp))
+        return emitAtUses(comp);
 
-            LCompareB *lir = new LCompareB(useRegisterOrConstant(right));
-            if (!useBox(lir, LCompareB::Lhs, left))
-                return false;
-            return define(lir, comp);
-        }
-
-        JS_ASSERT(IsNullOrUndefined(comp->specialization()));
-
+    // Compare Null and Undefined.
+    if (comp->compareType() == MCompare::Compare_Null ||
+        comp->compareType() == MCompare::Compare_Undefined)
+    {
         if (left->type() == MIRType_Object) {
             MOZ_ASSERT(comp->operandMightEmulateUndefined(),
                        "MCompare::tryFold should have folded this away");
 
             return define(new LEmulatesUndefined(useRegister(left)), comp);
         }
 
         LDefinition temp0, temp1;
@@ -589,22 +604,55 @@ LIRGenerator::visitCompare(MCompare *com
         }
 
         LIsNullOrLikeUndefined *lir = new LIsNullOrLikeUndefined(temp0, temp1);
         if (!useBox(lir, LIsNullOrLikeUndefined::Value, left))
             return false;
         return define(lir, comp);
     }
 
-    LCompareV *lir = new LCompareV();
-    if (!useBoxAtStart(lir, LCompareV::LhsInput, left))
-        return false;
-    if (!useBoxAtStart(lir, LCompareV::RhsInput, right))
-        return false;
-    return defineReturn(lir, comp) && assignSafepoint(lir, comp);
+    // Compare booleans.
+    if (comp->compareType() == MCompare::Compare_Boolean) {
+        JS_ASSERT(left->type() == MIRType_Value);
+        JS_ASSERT(right->type() == MIRType_Boolean);
+
+        LCompareB *lir = new LCompareB(useRegisterOrConstant(right));
+        if (!useBox(lir, LCompareB::Lhs, left))
+            return false;
+        return define(lir, comp);
+    }
+
+    // Compare Int32 or Object pointers.
+    if (comp->compareType() == MCompare::Compare_Int32 ||
+        comp->compareType() == MCompare::Compare_Object)
+    {
+        JSOp op = ReorderComparison(comp->jsop(), &left, &right);
+        LAllocation lhs = useRegister(left);
+        LAllocation rhs = useRegister(right);
+        if (comp->compareType() == MCompare::Compare_Int32)
+            rhs = useAnyOrConstant(right);
+        return define(new LCompare(op, lhs, rhs), comp);
+    }
+
+    // Compare doubles.
+    if (comp->compareType() == MCompare::Compare_Double)
+        return define(new LCompareD(useRegister(left), useRegister(right)), comp);
+
+    // Compare values.
+    if (comp->compareType() == MCompare::Compare_Value) {
+        LCompareV *lir = new LCompareV();
+        if (!useBoxAtStart(lir, LCompareV::LhsInput, left))
+            return false;
+        if (!useBoxAtStart(lir, LCompareV::RhsInput, right))
+            return false;
+        return define(lir, comp);
+    }
+
+    JS_NOT_REACHED("Unrecognized compare type.");
+    return false;
 }
 
 static void
 ReorderCommutative(MDefinition **lhsp, MDefinition **rhsp)
 {
     MDefinition *lhs = *lhsp;
     MDefinition *rhs = *rhsp;
 
--- a/js/src/ion/MIR.cpp
+++ b/js/src/ion/MIR.cpp
@@ -1079,110 +1079,213 @@ SafelyCoercesToDouble(JSContext *cx, typ
                                 types::TYPE_FLAG_INT32 | types::TYPE_FLAG_BOOLEAN;
 
     if ((flags & converts) == flags)
         return true;
 
     return false;
 }
 
+static bool
+CanDoValueBitwiseCmp(JSContext *cx, types::StackTypeSet *lhs, types::StackTypeSet *rhs, bool looseEq)
+{
+    // Only primitive (not double/string) or objects are supported.
+    // I.e. Undefined/Null/Boolean/Int32 and Object
+    if (!lhs->knownPrimitiveOrObject() ||
+        lhs->hasAnyFlag(types::TYPE_FLAG_STRING) ||
+        lhs->hasAnyFlag(types::TYPE_FLAG_DOUBLE) ||
+        !rhs->knownPrimitiveOrObject() ||
+        rhs->hasAnyFlag(types::TYPE_FLAG_STRING) ||
+        rhs->hasAnyFlag(types::TYPE_FLAG_DOUBLE))
+    {
+        return false;
+    }
+
+    // Objects with special equality or that emulates undefined are not supported.
+    if (lhs->maybeObject() &&
+        (lhs->hasObjectFlags(cx, types::OBJECT_FLAG_SPECIAL_EQUALITY) ||
+         lhs->hasObjectFlags(cx, types::OBJECT_FLAG_EMULATES_UNDEFINED)))
+    {
+        return false;
+    }
+    if (rhs->maybeObject() &&
+        (rhs->hasObjectFlags(cx, types::OBJECT_FLAG_SPECIAL_EQUALITY) ||
+         rhs->hasObjectFlags(cx, types::OBJECT_FLAG_EMULATES_UNDEFINED)))
+    {
+        return false;
+    }
+
+    // In the loose comparison more values could be the same,
+    // but value comparison reporting otherwise.
+    if (looseEq) {
+
+        // Undefined compared loosy to Null is not supported,
+        // because tag is different, but value can be the same (undefined == null).
+        if ((lhs->hasAnyFlag(types::TYPE_FLAG_UNDEFINED) &&
+             rhs->hasAnyFlag(types::TYPE_FLAG_NULL)) ||
+            (lhs->hasAnyFlag(types::TYPE_FLAG_NULL) &&
+             rhs->hasAnyFlag(types::TYPE_FLAG_UNDEFINED)))
+        {
+            return false;
+        }
+
+        // Int32 compared loosy to Boolean is not supported,
+        // because tag is different, but value can be the same (1 == true).
+        if ((lhs->hasAnyFlag(types::TYPE_FLAG_INT32) &&
+             rhs->hasAnyFlag(types::TYPE_FLAG_BOOLEAN)) ||
+            (lhs->hasAnyFlag(types::TYPE_FLAG_BOOLEAN) &&
+             rhs->hasAnyFlag(types::TYPE_FLAG_INT32)))
+        {
+            return false;
+        }
+
+        // For loosy comparison of an object with a Boolean/Number/String
+        // the valueOf the object is taken. Therefore not supported.
+        types::TypeFlags numbers = types::TYPE_FLAG_BOOLEAN |
+                                   types::TYPE_FLAG_INT32;
+        if ((lhs->maybeObject() && rhs->hasAnyFlag(numbers)) ||
+            (rhs->maybeObject() && lhs->hasAnyFlag(numbers)))
+        {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+MIRType
+MCompare::inputType()
+{
+    switch(compareType_) {
+      case Compare_Undefined:
+        return MIRType_Undefined;
+      case Compare_Null:
+        return MIRType_Null;
+      case Compare_Boolean:
+        return MIRType_Boolean;
+      case Compare_Int32:
+        return MIRType_Int32;
+      case Compare_Double:
+        return MIRType_Double;
+      case Compare_String:
+        return MIRType_String;
+      case Compare_Object:
+        return MIRType_Object;
+      case Compare_Unknown:
+      case Compare_Value:
+        return MIRType_Value;
+      default:
+        JS_NOT_REACHED("No known conversion");
+        return MIRType_None;
+    }
+}
+
 void
 MCompare::infer(const TypeOracle::BinaryTypes &b, JSContext *cx)
 {
     if (!b.lhsTypes || !b.rhsTypes)
         return;
 
     JS_ASSERT(operandMightEmulateUndefined());
 
     if (!MaybeEmulatesUndefined(b.lhsTypes, cx) && !MaybeEmulatesUndefined(b.rhsTypes, cx))
         markNoOperandEmulatesUndefined();
 
     MIRType lhs = MIRTypeFromValueType(b.lhsTypes->getKnownTypeTag());
     MIRType rhs = MIRTypeFromValueType(b.rhsTypes->getKnownTypeTag());
 
-    // Strict integer or boolean comparisons may be treated as Int32.
+    bool looseEq = jsop() == JSOP_EQ || jsop() == JSOP_NE;
+    bool strictEq = jsop() == JSOP_STRICTEQ || jsop() == JSOP_STRICTNE;
+    bool relationalEq = !(looseEq || strictEq);
+
+    // Integer to integer or boolean to boolean comparisons may be treated as Int32.
     if ((lhs == MIRType_Int32 && rhs == MIRType_Int32) ||
         (lhs == MIRType_Boolean && rhs == MIRType_Boolean))
     {
-        specialization_ = MIRType_Int32;
+        compareType_ = Compare_Int32;
         return;
     }
 
-    // Loose cross-integer/boolean comparisons may be treated as Int32.
-    if (jsop() != JSOP_STRICTEQ && jsop() != JSOP_STRICTNE &&
+    // Loose/relational cross-integer/boolean comparisons may be treated as Int32.
+    if (!strictEq &&
         (lhs == MIRType_Int32 || lhs == MIRType_Boolean) &&
         (rhs == MIRType_Int32 || rhs == MIRType_Boolean))
     {
-        specialization_ = MIRType_Int32;
+        compareType_ = Compare_Int32;
         return;
     }
 
     // Numeric comparisons against a double coerce to double.
     if (IsNumberType(lhs) && IsNumberType(rhs)) {
-        specialization_ = MIRType_Double;
+        compareType_ = Compare_Double;
         return;
     }
 
-    // Handle double comparisons against something that safely coerces to double.
-    if (jsop() != JSOP_STRICTEQ && jsop() != JSOP_STRICTNE &&
+    // Any comparison is allowed except strict eq.
+    if (!strictEq &&
         ((lhs == MIRType_Double && SafelyCoercesToDouble(cx, b.rhsTypes)) ||
          (rhs == MIRType_Double && SafelyCoercesToDouble(cx, b.lhsTypes))))
     {
-        specialization_ = MIRType_Double;
+        compareType_ = Compare_Double;
         return;
     }
 
-    if (jsop() == JSOP_STRICTEQ || jsop() == JSOP_EQ ||
-        jsop() == JSOP_STRICTNE || jsop() == JSOP_NE)
-    {
-        if (lhs == MIRType_Object && rhs == MIRType_Object) {
-            if (b.lhsTypes->hasObjectFlags(cx, types::OBJECT_FLAG_SPECIAL_EQUALITY) ||
-                b.rhsTypes->hasObjectFlags(cx, types::OBJECT_FLAG_SPECIAL_EQUALITY))
-            {
-                return;
-            }
-            specialization_ = MIRType_Object;
+    // Handle object comparison.
+    if (!relationalEq && lhs == MIRType_Object && rhs == MIRType_Object) {
+        if (b.lhsTypes->hasObjectFlags(cx, types::OBJECT_FLAG_SPECIAL_EQUALITY) ||
+            b.rhsTypes->hasObjectFlags(cx, types::OBJECT_FLAG_SPECIAL_EQUALITY))
+        {
             return;
         }
 
-        if (lhs == MIRType_String && rhs == MIRType_String) {
-            // We don't yet want to optimize relational string compares.
-            specialization_ = MIRType_String;
-            return;
-        }
+        compareType_ = Compare_Object;
+        return;
+    }
 
-        // Swap null/undefined lhs to rhs so we can test for it only on lhs.
-        if (IsNullOrUndefined(lhs)) {
-            MIRType tmp = lhs;
-            lhs = rhs;
-            rhs = tmp;
-            swapOperands();
-        }
-
-        if (IsNullOrUndefined(rhs)) {
-            specialization_ = rhs;
-            return;
-        }
+    // Handle string comparisons. (Relational string compares are still unsupported).
+    if (!relationalEq && lhs == MIRType_String && rhs == MIRType_String) {
+        compareType_ = Compare_String;
+        return;
     }
 
-    if (jsop() == JSOP_STRICTEQ || jsop() == JSOP_STRICTNE) {
+    // Handle compare with lhs being Undefined or Null.
+    if (!relationalEq && IsNullOrUndefined(lhs)) {
+        // Lowering expects the rhs to be null/undefined, so we have to
+        // swap the operands. This is necessary since we may not know which
+        // operand was null/undefined during lowering (both operands may have
+        // MIRType_Value).
+        compareType_ = (lhs == MIRType_Null) ? Compare_Null : Compare_Undefined;
+        swapOperands();
+        return;
+    }
+
+    // Handle compare with rhs being Undefined or Null.
+    if (!relationalEq && IsNullOrUndefined(rhs)) {
+        compareType_ = (rhs == MIRType_Null) ? Compare_Null : Compare_Undefined;
+        return;
+    }
+
+    // Handle strict comparison with lhs/rhs being typed Boolean.
+    if (strictEq && (lhs == MIRType_Boolean || rhs == MIRType_Boolean)) {
         // bool/bool case got an int32 specialization earlier.
         JS_ASSERT(!(lhs == MIRType_Boolean && rhs == MIRType_Boolean));
 
-        if (lhs == MIRType_Boolean) {
-            // Ensure the boolean is on the right so that the type policy knows
-            // which side to unbox.
-            swapOperands();
-            specialization_ = MIRType_Boolean;
-            return;
-        }
-        if (rhs == MIRType_Boolean) {
-            specialization_ = MIRType_Boolean;
-            return;
-        }
+        // Ensure the boolean is on the right so that the type policy knows
+        // which side to unbox.
+        if (lhs == MIRType_Boolean)
+             swapOperands();
+
+        compareType_ = Compare_Boolean;
+        return;
+    }
+
+    // Determine if we can do the compare based on a quick value check.
+    if (!relationalEq && CanDoValueBitwiseCmp(cx, b.lhsTypes, b.rhsTypes, looseEq)) {
+        compareType_ = Compare_Value;
+        return;
     }
 }
 
 MBitNot *
 MBitNot::New(MDefinition *input)
 {
     return new MBitNot(input);
 }
@@ -1390,27 +1493,27 @@ MClampToUint8::foldsTo(bool useValueNumb
     return this;
 }
 
 bool
 MCompare::tryFold(bool *result)
 {
     JSOp op = jsop();
 
-    if (IsNullOrUndefined(specialization())) {
+    if (compareType_ == Compare_Null || compareType_ == Compare_Undefined) {
         JS_ASSERT(op == JSOP_EQ || op == JSOP_STRICTEQ ||
                   op == JSOP_NE || op == JSOP_STRICTNE);
 
         // The LHS is the value we want to test against null or undefined.
         switch (lhs()->type()) {
           case MIRType_Value:
             return false;
           case MIRType_Undefined:
           case MIRType_Null:
-            if (lhs()->type() == specialization()) {
+            if (lhs()->type() == inputType()) {
                 // Both sides have the same type, null or undefined.
                 *result = (op == JSOP_EQ || op == JSOP_STRICTEQ);
             } else {
                 // One side is null, the other side is undefined. The result is only
                 // true for loose equality.
                 *result = (op == JSOP_EQ || op == JSOP_STRICTNE);
             }
             return true;
@@ -1425,17 +1528,17 @@ MCompare::tryFold(bool *result)
             *result = (op == JSOP_NE || op == JSOP_STRICTNE);
             return true;
           default:
             JS_NOT_REACHED("Unexpected type");
             return false;
         }
     }
 
-    if (specialization_ == MIRType_Boolean) {
+    if (compareType_ == Compare_Boolean) {
         JS_ASSERT(op == JSOP_STRICTEQ || op == JSOP_STRICTNE);
         JS_ASSERT(rhs()->type() == MIRType_Boolean);
 
         switch (lhs()->type()) {
           case MIRType_Value:
             return false;
           case MIRType_Int32:
           case MIRType_Double:
--- a/js/src/ion/MIR.h
+++ b/js/src/ion/MIR.h
@@ -1377,40 +1377,85 @@ class MTernaryInstruction : public MAryI
                third->valueNumber() == insThird->valueNumber();
     }
 };
 
 class MCompare
   : public MBinaryInstruction,
     public ComparePolicy
 {
+  public:
+    enum CompareType {
+
+        // Anything compared to Undefined
+        Compare_Undefined,
+
+        // Anything compared to Null
+        Compare_Null,
+
+        // Undefined compared to Boolean
+        // Null      compared to Boolean
+        // Double    compared to Boolean
+        // String    compared to Boolean
+        // Object    compared to Boolean
+        // Value     compared to Boolean
+        Compare_Boolean,
+
+        // Int32   compared to Int32
+        // Boolean compared to Boolean
+        Compare_Int32,
+
+        // Double compared to Double
+        Compare_Double,
+
+        // String compared to String
+        Compare_String,
+
+        // Object compared to Object
+        Compare_Object,
+
+        // Compare 2 values bitwise
+        Compare_Value,
+
+        // All other possible compares
+        Compare_Unknown
+    };
+
+  private:
+    CompareType compareType_;
     JSOp jsop_;
     bool operandMightEmulateUndefined_;
 
     MCompare(MDefinition *left, MDefinition *right, JSOp jsop)
       : MBinaryInstruction(left, right),
+        compareType_(Compare_Unknown),
         jsop_(jsop),
         operandMightEmulateUndefined_(true)
     {
         setResultType(MIRType_Boolean);
         setMovable();
     }
 
   public:
     INSTRUCTION_HEADER(Compare)
+
     static MCompare *New(MDefinition *left, MDefinition *right, JSOp op);
 
     bool tryFold(bool *result);
     bool evaluateConstantOperands(bool *result);
     MDefinition *foldsTo(bool useValueNumbers);
 
     void infer(const TypeOracle::BinaryTypes &b, JSContext *cx);
-    MIRType specialization() const {
-        return specialization_;
-    }
+    CompareType compareType() const {
+        return compareType_;
+    }
+    void setCompareType(CompareType type) {
+        compareType_ = type;
+    }
+    MIRType inputType();
 
     JSOp jsop() const {
         return jsop_;
     }
     TypePolicy *typePolicy() {
         return this;
     }
     void markNoOperandEmulatesUndefined() {
@@ -1418,19 +1463,19 @@ class MCompare
     }
     bool operandMightEmulateUndefined() const {
         return operandMightEmulateUndefined_;
     }
     AliasSet getAliasSet() const {
         // Strict equality is never effectful.
         if (jsop_ == JSOP_STRICTEQ || jsop_ == JSOP_STRICTNE)
             return AliasSet::None();
-        if (specialization_ == MIRType_None)
+        if (compareType_ == Compare_Unknown)
             return AliasSet::Store(AliasSet::Any);
-        JS_ASSERT(specialization_ <= MIRType_Object);
+        JS_ASSERT(compareType_ <= Compare_Object);
         return AliasSet::None();
     }
 
   protected:
     bool congruentTo(MDefinition *const &ins) const {
         if (!MBinaryInstruction::congruentTo(ins))
             return false;
         return jsop() == ins->toCompare()->jsop();
--- a/js/src/ion/TypePolicy.cpp
+++ b/js/src/ion/TypePolicy.cpp
@@ -91,66 +91,71 @@ BinaryStringPolicy::adjustInputs(MInstru
     }
 
     return true;
 }
 
 bool
 ComparePolicy::adjustInputs(MInstruction *def)
 {
-    if (specialization_ == MIRType_None)
+    JS_ASSERT(def->isCompare());
+    MCompare *compare = def->toCompare();
+    MIRType type = compare->inputType();
+
+    // Box inputs to get value
+    if (type == MIRType_Value)
         return BoxInputsPolicy::adjustInputs(def);
 
-    if (IsNullOrUndefined(specialization_)) {
-        // Nothing to do, lowering handles all types.
+    // Nothing to do for undefined and null, lowering handles all types.
+    if (type == MIRType_Undefined || type == MIRType_Null)
         return true;
+
+    // MIRType_Boolean specialization is done for "Anything === Bool"
+    // If the LHS is boolean, we set the specialization to Compare_Int32.
+    // This matches other comparisons of the form bool === bool and
+    // generated code of Compare_Int32 is more efficient.
+    if (type == MIRType_Boolean && def->getOperand(0)->type() == MIRType_Boolean) {
+       compare->setCompareType(MCompare::Compare_Int32);
+       type = compare->inputType();
     }
 
-    if (specialization_ == MIRType_Boolean) {
-        // The RHS is boolean, unbox if needed.
+    // MIRType_Boolean specialization is done for "Anything === Bool"
+    // As of previous line Anything can't be Boolean
+    if (type == MIRType_Boolean) {
+        // Unbox rhs that is definitely Boolean
         MDefinition *rhs = def->getOperand(1);
 
         if (rhs->type() == MIRType_Value) {
             MInstruction *unbox = MUnbox::New(rhs, MIRType_Boolean, MUnbox::Infallible);
             def->block()->insertBefore(def, unbox);
             def->replaceOperand(1, unbox);
         }
 
+        JS_ASSERT(def->getOperand(0)->type() != MIRType_Boolean);
         JS_ASSERT(def->getOperand(1)->type() == MIRType_Boolean);
-
-        // Allow the LHS to have any type other than boolean. Value === boolean
-        // is handled by LCompareB, comparisons with other non-boolean types are
-        // folded.
-        if (def->getOperand(0)->type() != MIRType_Boolean)
-            return true;
-
-        // If the LHS is boolean, we set the specialization to int32 and
-        // fall-through. This matches other comparisons of the form
-        // bool === bool and allows us to use LCompare, which is much more
-        // efficient than LCompareB.
-        specialization_ = MIRType_Int32;
+        return true;
     }
 
+    // Convert all inputs to the right input type
     for (size_t i = 0; i < 2; i++) {
         MDefinition *in = def->getOperand(i);
-        if (in->type() == specialization_)
+        if (in->type() == type)
             continue;
 
         MInstruction *replace;
 
         // See BinaryArithPolicy::adjustInputs for an explanation of the following
         if (in->type() == MIRType_Object || in->type() == MIRType_String)
             in = boxAt(def, in);
 
-        switch (specialization_) {
+        switch (type) {
           case MIRType_Double:
             replace = MToDouble::New(in);
             break;
           case MIRType_Int32:
-          case MIRType_Boolean:
             replace = MToInt32::New(in);
             break;
           case MIRType_Object:
             replace = MUnbox::New(in, MIRType_Object, MUnbox::Infallible);
             break;
           case MIRType_String:
             replace = MUnbox::New(in, MIRType_String, MUnbox::Infallible);
             break;
--- a/js/src/ion/TypePolicy.h
+++ b/js/src/ion/TypePolicy.h
@@ -72,24 +72,16 @@ class BitwisePolicy : public BoxInputsPo
 
     MIRType specialization() const {
         return specialization_;
     }
 };
 
 class ComparePolicy : public BoxInputsPolicy
 {
-  protected:
-    MIRType specialization_;
-
-  public:
-    ComparePolicy()
-      : specialization_(MIRType_None)
-    { }
-
     bool adjustInputs(MInstruction *def);
 };
 
 // Policy for MTest instructions.
 class TestPolicy : public BoxInputsPolicy
 {
   public:
     bool adjustInputs(MInstruction *ins);
--- a/js/src/ion/arm/CodeGenerator-arm.cpp
+++ b/js/src/ion/arm/CodeGenerator-arm.cpp
@@ -1226,16 +1226,69 @@ CodeGeneratorARM::visitCompareBAndBranch
         masm.cmp32(lhs.payloadReg(), Imm32(rhs->toConstant()->toBoolean()));
     else
         masm.cmp32(lhs.payloadReg(), ToRegister(rhs));
     emitBranch(JSOpToCondition(mir->jsop()), lir->ifTrue(), lir->ifFalse());
     return true;
 }
 
 bool
+CodeGeneratorARM::visitCompareV(LCompareV *lir)
+{
+    MCompare *mir = lir->mir();
+    Assembler::Condition cond = JSOpToCondition(mir->jsop());
+    const ValueOperand lhs = ToValue(lir, LCompareV::LhsInput);
+    const ValueOperand rhs = ToValue(lir, LCompareV::RhsInput);
+    const Register output = ToRegister(lir->output());
+
+    JS_ASSERT(mir->jsop() == JSOP_EQ || mir->jsop() == JSOP_STRICTEQ ||
+              mir->jsop() == JSOP_NE || mir->jsop() == JSOP_STRICTNE);
+
+    Label notEqual, done;
+    masm.cmp32(lhs.typeReg(), rhs.typeReg());
+    masm.j(Assembler::NotEqual, &notEqual);
+    {
+        masm.cmp32(lhs.payloadReg(), rhs.payloadReg());
+        emitSet(cond, output);
+        masm.jump(&done);
+    }
+    masm.bind(&notEqual);
+    {
+        masm.move32(Imm32(cond == Assembler::NotEqual), output);
+    }
+
+    masm.bind(&done);
+    return true;
+}
+
+bool
+CodeGeneratorARM::visitCompareVAndBranch(LCompareVAndBranch *lir)
+{
+    MCompare *mir = lir->mir();
+    Assembler::Condition cond = JSOpToCondition(mir->jsop());
+    const ValueOperand lhs = ToValue(lir, LCompareVAndBranch::LhsInput);
+    const ValueOperand rhs = ToValue(lir, LCompareVAndBranch::RhsInput);
+
+    JS_ASSERT(mir->jsop() == JSOP_EQ || mir->jsop() == JSOP_STRICTEQ ||
+              mir->jsop() == JSOP_NE || mir->jsop() == JSOP_STRICTNE);
+
+    Label *notEqual;
+    if (cond == Assembler::Equal)
+        notEqual = lir->ifFalse()->lir()->label();
+    else
+        notEqual = lir->ifTrue()->lir()->label();
+
+    masm.cmp32(lhs.typeReg(), rhs.typeReg());
+    masm.j(Assembler::NotEqual, notEqual);
+    masm.cmp32(lhs.payloadReg(), rhs.payloadReg());
+    emitBranch(cond, lir->ifTrue(), lir->ifFalse());
+
+    return true;
+}
+bool
 CodeGeneratorARM::visitNotI(LNotI *ins)
 {
     // It is hard to optimize !x, so just do it the basic way for now.
     masm.ma_cmp(ToRegister(ins->input()), Imm32(0));
     emitSet(Assembler::Equal, ToRegister(ins->output()));
     return true;
 }
 
--- a/js/src/ion/arm/CodeGenerator-arm.h
+++ b/js/src/ion/arm/CodeGenerator-arm.h
@@ -90,16 +90,18 @@ class CodeGeneratorARM : public CodeGene
     virtual bool visitTestIAndBranch(LTestIAndBranch *test);
     virtual bool visitCompare(LCompare *comp);
     virtual bool visitCompareAndBranch(LCompareAndBranch *comp);
     virtual bool visitTestDAndBranch(LTestDAndBranch *test);
     virtual bool visitCompareD(LCompareD *comp);
     virtual bool visitCompareDAndBranch(LCompareDAndBranch *comp);
     virtual bool visitCompareB(LCompareB *lir);
     virtual bool visitCompareBAndBranch(LCompareBAndBranch *lir);
+    virtual bool visitCompareV(LCompareV *lir);
+    virtual bool visitCompareVAndBranch(LCompareVAndBranch *lir);
     virtual bool visitNotI(LNotI *ins);
     virtual bool visitNotD(LNotD *ins);
 
     virtual bool visitMathD(LMathD *math);
     virtual bool visitFloor(LFloor *lir);
     virtual bool visitRound(LRound *lir);
     virtual bool visitTruncateDToInt32(LTruncateDToInt32 *ins);
 
--- a/js/src/ion/shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/ion/shared/CodeGenerator-x86-shared.cpp
@@ -175,43 +175,43 @@ CodeGeneratorX86Shared::emitSet(Assemble
         masm.bind(&ifFalse);
         masm.xorl(dest, dest);
 
         masm.bind(&end);
     }
 }
 
 void
-CodeGeneratorX86Shared::emitCompare(MIRType type, const LAllocation *left, const LAllocation *right)
+CodeGeneratorX86Shared::emitCompare(MCompare::CompareType type, const LAllocation *left, const LAllocation *right)
 {
 #ifdef JS_CPU_X64
-    if (type == MIRType_Object) {
+    if (type == MCompare::Compare_Object) {
         masm.cmpq(ToRegister(left), ToOperand(right));
         return;
     }
 #endif
 
     if (right->isConstant())
         masm.cmpl(ToRegister(left), Imm32(ToInt32(right)));
     else
         masm.cmpl(ToRegister(left), ToOperand(right));
 }
 
 bool
 CodeGeneratorX86Shared::visitCompare(LCompare *comp)
 {
-    emitCompare(comp->mir()->specialization(), comp->left(), comp->right());
+    emitCompare(comp->mir()->compareType(), comp->left(), comp->right());
     emitSet(JSOpToCondition(comp->jsop()), ToRegister(comp->output()));
     return true;
 }
 
 bool
 CodeGeneratorX86Shared::visitCompareAndBranch(LCompareAndBranch *comp)
 {
-    emitCompare(comp->mir()->specialization(), comp->left(), comp->right());
+    emitCompare(comp->mir()->compareType(), comp->left(), comp->right());
     Assembler::Condition cond = JSOpToCondition(comp->jsop());
     emitBranch(cond, comp->ifTrue(), comp->ifFalse());
     return true;
 }
 
 bool
 CodeGeneratorX86Shared::visitCompareD(LCompareD *comp)
 {
--- a/js/src/ion/shared/CodeGenerator-x86-shared.h
+++ b/js/src/ion/shared/CodeGenerator-x86-shared.h
@@ -58,17 +58,17 @@ class CodeGeneratorX86Shared : public Co
     bool generatePrologue();
     bool generateEpilogue();
     bool generateOutOfLineCode();
 
     void emitDoubleToInt32(const FloatRegister &src, const Register &dest, Label *fail, bool negativeZeroCheck = true);
 
     Operand createArrayElementOperand(Register elements, const LAllocation *index);
 
-    void emitCompare(MIRType type, const LAllocation *left, const LAllocation *right);
+    void emitCompare(MCompare::CompareType type, const LAllocation *left, const LAllocation *right);
 
     // Emits a conditional set.
     void emitSet(Assembler::Condition cond, const Register &dest,
                  Assembler::NaNCond ifNaN = Assembler::NaN_Unexpected);
     void emitSet(Assembler::DoubleCondition cond, const Register &dest);
 
     // Emits a branch that directs control flow to the true block if |cond| is
     // true, and the false block if |cond| is false.
--- a/js/src/ion/x64/CodeGenerator-x64.cpp
+++ b/js/src/ion/x64/CodeGenerator-x64.cpp
@@ -356,8 +356,39 @@ CodeGeneratorX64::visitCompareBAndBranch
     else
         masm.boxValue(JSVAL_TYPE_BOOLEAN, ToRegister(rhs), ScratchReg);
 
     // Perform the comparison.
     masm.cmpq(lhs.valueReg(), ScratchReg);
     emitBranch(JSOpToCondition(mir->jsop()), lir->ifTrue(), lir->ifFalse());
     return true;
 }
+bool
+CodeGeneratorX64::visitCompareV(LCompareV *lir)
+{
+    MCompare *mir = lir->mir();
+    const ValueOperand lhs = ToValue(lir, LCompareV::LhsInput);
+    const ValueOperand rhs = ToValue(lir, LCompareV::RhsInput);
+    const Register output = ToRegister(lir->output());
+
+    JS_ASSERT(mir->jsop() == JSOP_EQ || mir->jsop() == JSOP_STRICTEQ ||
+              mir->jsop() == JSOP_NE || mir->jsop() == JSOP_STRICTNE);
+
+    masm.cmpq(lhs.valueReg(), rhs.valueReg());
+    emitSet(JSOpToCondition(mir->jsop()), output);
+    return true;
+}
+
+bool
+CodeGeneratorX64::visitCompareVAndBranch(LCompareVAndBranch *lir)
+{
+    MCompare *mir = lir->mir();
+
+    const ValueOperand lhs = ToValue(lir, LCompareVAndBranch::LhsInput);
+    const ValueOperand rhs = ToValue(lir, LCompareVAndBranch::RhsInput);
+
+    JS_ASSERT(mir->jsop() == JSOP_EQ || mir->jsop() == JSOP_STRICTEQ ||
+              mir->jsop() == JSOP_NE || mir->jsop() == JSOP_STRICTNE);
+
+    masm.cmpq(lhs.valueReg(), rhs.valueReg());
+    emitBranch(JSOpToCondition(mir->jsop()), lir->ifTrue(), lir->ifFalse());
+    return true;
+}
--- a/js/src/ion/x64/CodeGenerator-x64.h
+++ b/js/src/ion/x64/CodeGenerator-x64.h
@@ -46,16 +46,18 @@ class CodeGeneratorX64 : public CodeGene
     bool visitLoadSlotT(LLoadSlotT *load);
     bool visitStoreSlotT(LStoreSlotT *store);
     bool visitLoadElementT(LLoadElementT *load);
     bool visitImplicitThis(LImplicitThis *lir);
     bool visitRecompileCheck(LRecompileCheck *lir);
     bool visitInterruptCheck(LInterruptCheck *lir);
     bool visitCompareB(LCompareB *lir);
     bool visitCompareBAndBranch(LCompareBAndBranch *lir);
+    bool visitCompareV(LCompareV *lir);
+    bool visitCompareVAndBranch(LCompareVAndBranch *lir);
 };
 
 typedef CodeGeneratorX64 CodeGeneratorSpecific;
 
 } // namespace ion
 } // namespace js
 
 #endif // jsion_codegen_x64_h__
--- a/js/src/ion/x86/CodeGenerator-x86.cpp
+++ b/js/src/ion/x86/CodeGenerator-x86.cpp
@@ -365,8 +365,62 @@ CodeGeneratorX86::visitCompareBAndBranch
 
     if (rhs->isConstant())
         masm.cmp32(lhs.payloadReg(), Imm32(rhs->toConstant()->toBoolean()));
     else
         masm.cmp32(lhs.payloadReg(), ToRegister(rhs));
     emitBranch(JSOpToCondition(mir->jsop()), lir->ifTrue(), lir->ifFalse());
     return true;
 }
+
+bool
+CodeGeneratorX86::visitCompareV(LCompareV *lir)
+{
+    MCompare *mir = lir->mir();
+    Assembler::Condition cond = JSOpToCondition(mir->jsop());
+    const ValueOperand lhs = ToValue(lir, LCompareV::LhsInput);
+    const ValueOperand rhs = ToValue(lir, LCompareV::RhsInput);
+    const Register output = ToRegister(lir->output());
+
+    JS_ASSERT(mir->jsop() == JSOP_EQ || mir->jsop() == JSOP_STRICTEQ ||
+              mir->jsop() == JSOP_NE || mir->jsop() == JSOP_STRICTNE);
+
+    Label notEqual, done;
+    masm.cmp32(lhs.typeReg(), rhs.typeReg());
+    masm.j(Assembler::NotEqual, &notEqual);
+    {
+        masm.cmp32(lhs.payloadReg(), rhs.payloadReg());
+        emitSet(cond, output);
+        masm.jump(&done);
+    }
+    masm.bind(&notEqual);
+    {
+        masm.move32(Imm32(cond == Assembler::NotEqual), output);
+    }
+
+    masm.bind(&done);
+    return true;
+}
+
+bool
+CodeGeneratorX86::visitCompareVAndBranch(LCompareVAndBranch *lir)
+{
+    MCompare *mir = lir->mir();
+    Assembler::Condition cond = JSOpToCondition(mir->jsop());
+    const ValueOperand lhs = ToValue(lir, LCompareVAndBranch::LhsInput);
+    const ValueOperand rhs = ToValue(lir, LCompareVAndBranch::RhsInput);
+
+    JS_ASSERT(mir->jsop() == JSOP_EQ || mir->jsop() == JSOP_STRICTEQ ||
+              mir->jsop() == JSOP_NE || mir->jsop() == JSOP_STRICTNE);
+
+    Label *notEqual;
+    if (cond == Assembler::Equal)
+        notEqual = lir->ifFalse()->lir()->label();
+    else
+        notEqual = lir->ifTrue()->lir()->label();
+
+    masm.cmp32(lhs.typeReg(), rhs.typeReg());
+    masm.j(Assembler::NotEqual, notEqual);
+    masm.cmp32(lhs.payloadReg(), rhs.payloadReg());
+    emitBranch(cond, lir->ifTrue(), lir->ifFalse());
+
+    return true;
+}
--- a/js/src/ion/x86/CodeGenerator-x86.h
+++ b/js/src/ion/x86/CodeGenerator-x86.h
@@ -65,16 +65,18 @@ class CodeGeneratorX86 : public CodeGene
     bool visitLoadSlotT(LLoadSlotT *load);
     bool visitStoreSlotT(LStoreSlotT *store);
     bool visitLoadElementT(LLoadElementT *load);
     bool visitImplicitThis(LImplicitThis *lir);
     bool visitRecompileCheck(LRecompileCheck *lir);
     bool visitInterruptCheck(LInterruptCheck *lir);
     bool visitCompareB(LCompareB *lir);
     bool visitCompareBAndBranch(LCompareBAndBranch *lir);
+    bool visitCompareV(LCompareV *lir);
+    bool visitCompareVAndBranch(LCompareVAndBranch *lir);
 };
 
 typedef CodeGeneratorX86 CodeGeneratorSpecific;
 
 } // namespace ion
 } // namespace js
 
 #endif // jsion_codegen_x86_h__
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -604,16 +604,26 @@ class StackTypeSet : public TypeSet
     /* Whether any objects in the type set needs a barrier on id. */
     bool propertyNeedsBarrier(JSContext *cx, jsid id);
 
     /*
      * Get whether this type only contains non-string primitives:
      * null/undefined/int/double, or some combination of those.
      */
     bool knownNonStringPrimitive();
+
+    bool knownPrimitiveOrObject() {
+        TypeFlags flags = TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL | TYPE_FLAG_DOUBLE |
+                          TYPE_FLAG_INT32 | TYPE_FLAG_BOOLEAN | TYPE_FLAG_STRING |
+                          TYPE_FLAG_ANYOBJECT;
+        if (baseFlags() & (~flags & TYPE_FLAG_BASE_MASK))
+            return false;
+
+        return true;
+    }
 };
 
 /*
  * Type set for a property of a TypeObject, or for the return value or property
  * read inputs of a script. In contrast with stack type sets, constraints on
  * these sets are not cleared during analysis purges, and are not implicitly
  * frozen during compilation.
  */