Bug 1176288 - SharedStubs: Part 2: Fix compare shared stub to work in ionmonkey, r=jandem
authorHannes Verschore <hv1989@gmail.com>
Thu, 10 Sep 2015 14:47:20 +0200
changeset 294388 9eb98b244b6d2cf3564abea148f8069788167a2c
parent 294387 be1b57ffbb743c3403b9702ebdb830c563993fef
child 294389 a530b8b339b4967d2935be5af7242aed35b982d1
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1176288
milestone43.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 1176288 - SharedStubs: Part 2: Fix compare shared stub to work in ionmonkey, r=jandem
js/src/jit/BaselineCompiler.cpp
js/src/jit/CodeGenerator.cpp
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jit/RangeAnalysis.cpp
js/src/jit/SharedIC.cpp
js/src/jit/SharedIC.h
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -1705,17 +1705,17 @@ bool
 BaselineCompiler::emitCompare()
 {
     // CODEGEN
 
     // Keep top JSStack value in R0 and R1.
     frame.popRegsAndSync(2);
 
     // Call IC.
-    ICCompare_Fallback::Compiler stubCompiler(cx);
+    ICCompare_Fallback::Compiler stubCompiler(cx, ICStubCompiler::Engine::Baseline);
     if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
         return false;
 
     // Mark R0 as pushed stack value.
     frame.push(R0, JSVAL_TYPE_BOOLEAN);
     return true;
 }
 
@@ -1740,17 +1740,17 @@ BaselineCompiler::emit_JSOP_CONDSWITCH()
 bool
 BaselineCompiler::emit_JSOP_CASE()
 {
     frame.popRegsAndSync(2);
     frame.push(R0);
     frame.syncStack(0);
 
     // Call IC.
-    ICCompare_Fallback::Compiler stubCompiler(cx);
+    ICCompare_Fallback::Compiler stubCompiler(cx, ICStubCompiler::Engine::Baseline);
     if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
         return false;
 
     Register payload = masm.extractInt32(R0, R0.scratchReg());
     jsbytecode* target = pc + GET_JUMP_OFFSET(pc);
 
     Label done;
     masm.branch32(Assembler::Equal, payload, Imm32(0), &done);
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -1716,25 +1716,35 @@ CodeGenerator::emitSharedStub(ICStub::Ki
     masm.freeStack(sizeof(intptr_t));
 #endif
     markSafepointAt(callOffset, lir);
 }
 
 void
 CodeGenerator::visitBinarySharedStub(LBinarySharedStub* lir)
 {
-    JSOp jsop = JSOp(*lir->mir()->resumePoint()->pc());
+    JSOp jsop = JSOp(*lir->mirRaw()->toInstruction()->resumePoint()->pc());
     switch (jsop) {
       case JSOP_ADD:
       case JSOP_SUB:
       case JSOP_MUL:
       case JSOP_DIV:
       case JSOP_MOD:
         emitSharedStub(ICStub::Kind::BinaryArith_Fallback, lir);
         break;
+      case JSOP_LT:
+      case JSOP_LE:
+      case JSOP_GT:
+      case JSOP_GE:
+      case JSOP_EQ:
+      case JSOP_NE:
+      case JSOP_STRICTEQ:
+      case JSOP_STRICTNE:
+        emitSharedStub(ICStub::Kind::Compare_Fallback, lir);
+        break;
       default:
         MOZ_CRASH("Unsupported jsop in shared stubs.");
     }
 }
 
 void
 CodeGenerator::visitUnarySharedStub(LUnarySharedStub* lir)
 {
@@ -7883,16 +7893,21 @@ CodeGenerator::linkSharedStubs(JSContext
             stub = stubCompiler.getStub(&stubSpace_);
             break;
           }
           case ICStub::Kind::UnaryArith_Fallback: {
             ICUnaryArith_Fallback::Compiler stubCompiler(cx, ICStubCompiler::Engine::IonMonkey);
             stub = stubCompiler.getStub(&stubSpace_);
             break;
           }
+          case ICStub::Kind::Compare_Fallback: {
+            ICCompare_Fallback::Compiler stubCompiler(cx, ICStubCompiler::Engine::IonMonkey);
+            stub = stubCompiler.getStub(&stubSpace_);
+            break;
+          }
           default:
             MOZ_CRASH("Unsupported shared stub.");
         }
 
         if (!stub)
             return false;
 
         sharedStubs_[i].entry.setFirstStub(stub);
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -4060,20 +4060,21 @@ IonBuilder::processCondSwitchCase(CFGSta
     if (!caseBlock)
         return ControlStatus_Error;
 
     // Terminate the last case condition block by emitting the code
     // corresponding to JSOP_CASE bytecode.
     if (bodyBlock != caseBlock) {
         MDefinition* caseOperand = current->pop();
         MDefinition* switchOperand = current->peek(-1);
-        MCompare* cmpResult = MCompare::New(alloc(), switchOperand, caseOperand, JSOP_STRICTEQ);
-        cmpResult->infer(constraints(), inspector, pc);
+
+        if (jsop_compare(JSOP_STRICTEQ, switchOperand, caseOperand))
+            return ControlStatus_Error;
+        MInstruction* cmpResult = current->pop()->toInstruction();
         MOZ_ASSERT(!cmpResult->isEffectful());
-        current->add(cmpResult);
         current->end(newTest(cmpResult, bodyBlock, caseBlock));
 
         // Add last case as predecessor of the body if the body is aliasing
         // the previous case body.
         if (!bodyIsNew && !bodyBlock->addPredecessorPopN(alloc(), current, 1))
             return ControlStatus_Error;
 
         // Add last case as predecessor of the non-matching case if the
@@ -6691,24 +6692,212 @@ IonBuilder::jsop_eval(uint32_t argc)
 }
 
 bool
 IonBuilder::jsop_compare(JSOp op)
 {
     MDefinition* right = current->pop();
     MDefinition* left = current->pop();
 
+    return jsop_compare(op, left, right);
+}
+
+bool
+IonBuilder::jsop_compare(JSOp op, MDefinition* left, MDefinition* right)
+{
+    bool emitted = false;
+
+    if (!forceInlineCaches()) {
+        if (!compareTrySpecialized(&emitted, op, left, right) || emitted)
+            return emitted;
+        if (!compareTryBitwise(&emitted, op, left, right) || emitted)
+            return emitted;
+        if (!compareTrySpecializedOnBaselineInspector(&emitted, op, left, right) || emitted)
+            return emitted;
+    }
+
+    if (!compareTrySharedStub(&emitted, op, left, right) || emitted)
+        return emitted;
+
+    // Not possible to optimize. Do a slow vm call.
     MCompare* ins = MCompare::New(alloc(), left, right, op);
+    ins->cacheOperandMightEmulateUndefined(constraints());
+
+    current->add(ins);
+    current->push(ins);
+    if (ins->isEffectful() && !resumeAfter(ins))
+        return false;
+    return true;
+}
+
+static bool
+ObjectOrSimplePrimitive(MDefinition* op)
+{
+    // Return true if op is either undefined/null/boolean/int32 or an object.
+    return !op->mightBeType(MIRType_String)
+        && !op->mightBeType(MIRType_Symbol)
+        && !op->mightBeType(MIRType_Double)
+        && !op->mightBeType(MIRType_Float32)
+        && !op->mightBeType(MIRType_MagicOptimizedArguments)
+        && !op->mightBeType(MIRType_MagicHole)
+        && !op->mightBeType(MIRType_MagicIsConstructing);
+}
+
+bool
+IonBuilder::compareTrySpecialized(bool* emitted, JSOp op, MDefinition* left, MDefinition* right)
+{
+    MOZ_ASSERT(*emitted == false);
+
+    // Try to emit an compare based on the input types.
+
+    MCompare::CompareType type = MCompare::determineCompareType(op, left, right);
+    if (type == MCompare::Compare_Unknown)
+        return true;
+
+    MCompare* ins = MCompare::New(alloc(), left, right, op);
+    ins->setCompareType(type);
+    ins->cacheOperandMightEmulateUndefined(constraints());
+
+    // Some compare types need to have the specific type in the rhs.
+    // Swap operands if that is not the case.
+    if (type == MCompare::Compare_StrictString && right->type() != MIRType_String)
+        ins->swapOperands();
+    else if (type == MCompare::Compare_Null && right->type() != MIRType_Null)
+        ins->swapOperands();
+    else if (type == MCompare::Compare_Undefined && right->type() != MIRType_Undefined)
+        ins->swapOperands();
+    else if (type == MCompare::Compare_Boolean && right->type() != MIRType_Boolean)
+        ins->swapOperands();
+
+    // Replace inputs with unsigned variants if needed.
+    if (type == MCompare::Compare_UInt32)
+        ins->replaceWithUnsignedOperands();
+
     current->add(ins);
     current->push(ins);
 
-    ins->infer(constraints(), inspector, pc);
-
-    if (ins->isEffectful() && !resumeAfter(ins))
-        return false;
+    MOZ_ASSERT(!ins->isEffectful());
+    *emitted = true;
+    return true;
+}
+
+bool
+IonBuilder::compareTryBitwise(bool* emitted, JSOp op, MDefinition* left, MDefinition* right)
+{
+    MOZ_ASSERT(*emitted == false);
+
+    // Try to emit a bitwise compare. Check if a bitwise compare equals the wanted
+    // result for all observed operand types.
+
+    // Onlye allow loose and strict equality.
+    if (op != JSOP_EQ && op != JSOP_NE && op != JSOP_STRICTEQ && op != JSOP_STRICTNE)
+        return true;
+
+    // Only primitive (not double/string) or objects are supported.
+    // I.e. Undefined/Null/Boolean/Int32 and Object
+    if (!ObjectOrSimplePrimitive(left) || !ObjectOrSimplePrimitive(right))
+        return true;
+
+    // Objects that emulate undefined are not supported.
+    if (left->maybeEmulatesUndefined(constraints()) || right->maybeEmulatesUndefined(constraints()))
+        return true;
+
+    // In the loose comparison more values could be the same,
+    // but value comparison reporting otherwise.
+    if (op == JSOP_EQ || op == JSOP_NE) {
+
+        // Undefined compared loosy to Null is not supported,
+        // because tag is different, but value can be the same (undefined == null).
+        if ((left->mightBeType(MIRType_Undefined) && right->mightBeType(MIRType_Null)) ||
+            (left->mightBeType(MIRType_Null) && right->mightBeType(MIRType_Undefined)))
+        {
+            return true;
+        }
+
+        // Int32 compared loosy to Boolean is not supported,
+        // because tag is different, but value can be the same (1 == true).
+        if ((left->mightBeType(MIRType_Int32) && right->mightBeType(MIRType_Boolean)) ||
+            (left->mightBeType(MIRType_Boolean) && right->mightBeType(MIRType_Int32)))
+        {
+            return true;
+        }
+
+        // For loosy comparison of an object with a Boolean/Number/String
+        // the valueOf the object is taken. Therefore not supported.
+        bool simpleLHS = left->mightBeType(MIRType_Boolean) || left->mightBeType(MIRType_Int32);
+        bool simpleRHS = right->mightBeType(MIRType_Boolean) || right->mightBeType(MIRType_Int32);
+        if ((left->mightBeType(MIRType_Object) && simpleRHS) ||
+            (right->mightBeType(MIRType_Object) && simpleLHS))
+        {
+            return true;
+        }
+    }
+
+    MCompare* ins = MCompare::New(alloc(), left, right, op);
+    ins->setCompareType(MCompare::Compare_Value);
+    ins->cacheOperandMightEmulateUndefined(constraints());
+
+    current->add(ins);
+    current->push(ins);
+
+    MOZ_ASSERT(!ins->isEffectful());
+    *emitted = true;
+    return true;
+}
+
+bool
+IonBuilder::compareTrySpecializedOnBaselineInspector(bool* emitted, JSOp op, MDefinition* left,
+                                                     MDefinition* right)
+{
+    MOZ_ASSERT(*emitted == false);
+
+    // Try to specialize based on any baseline caches that have been generated
+    // for the opcode. These will cause the instruction's type policy to insert
+    // fallible unboxes to the appropriate input types.
+
+    MCompare::CompareType type = inspector->expectedCompareType(pc);
+    if (type == MCompare::Compare_Unknown)
+        return true;
+
+    MCompare* ins = MCompare::New(alloc(), left, right, op);
+    ins->setCompareType(type);
+    ins->cacheOperandMightEmulateUndefined(constraints());
+
+    current->add(ins);
+    current->push(ins);
+
+    MOZ_ASSERT(!ins->isEffectful());
+    *emitted = true;
+    return true;
+}
+
+bool
+IonBuilder::compareTrySharedStub(bool* emitted, JSOp op, MDefinition* left, MDefinition* right)
+{
+    MOZ_ASSERT(*emitted == false);
+
+    // Try to emit a shared stub cache.
+
+    if (js_JitOptions.disableSharedStubs)
+        return true;
+
+    if (JSOp(*pc) == JSOP_CASE)
+        return true;
+
+    MBinarySharedStub* stub = MBinarySharedStub::New(alloc(), left, right);
+    current->add(stub);
+    current->push(stub);
+    if (!resumeAfter(stub))
+        return false;
+
+    MUnbox* unbox = MUnbox::New(alloc(), current->pop(), MIRType_Boolean, MUnbox::Infallible);
+    current->add(unbox);
+    current->push(unbox);
+
+    *emitted = true;
     return true;
 }
 
 bool
 IonBuilder::jsop_newarray(uint32_t count)
 {
     JSObject* templateObject = inspector->getTemplateObject(pc);
     gc::InitialHeap heap;
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -491,16 +491,23 @@ class IonBuilder
     bool binaryArithTrySpecialized(bool* emitted, JSOp op, MDefinition* left, MDefinition* right);
     bool binaryArithTrySpecializedOnBaselineInspector(bool* emitted, JSOp op, MDefinition* left,
                                                       MDefinition* right);
     bool arithTrySharedStub(bool* emitted, JSOp op, MDefinition* left, MDefinition* right);
 
     // jsop_bitnot helpers.
     bool bitnotTrySpecialized(bool* emitted, MDefinition* input);
 
+    // jsop_compare helpes.
+    bool compareTrySpecialized(bool* emitted, JSOp op, MDefinition* left, MDefinition* right);
+    bool compareTryBitwise(bool* emitted, JSOp op, MDefinition* left, MDefinition* right);
+    bool compareTrySpecializedOnBaselineInspector(bool* emitted, JSOp op, MDefinition* left,
+                                                  MDefinition* right);
+    bool compareTrySharedStub(bool* emitted, JSOp op, MDefinition* left, MDefinition* right);
+
     // binary data lookup helpers.
     TypedObjectPrediction typedObjectPrediction(MDefinition* typedObj);
     TypedObjectPrediction typedObjectPrediction(TemporaryTypeSet* types);
     bool typedObjectHasField(MDefinition* typedObj,
                              PropertyName* name,
                              size_t* fieldOffset,
                              TypedObjectPrediction* fieldTypeReprs,
                              size_t* fieldIndex);
@@ -656,16 +663,17 @@ class IonBuilder
     bool jsop_ifeq(JSOp op);
     bool jsop_try();
     bool jsop_label();
     bool jsop_condswitch();
     bool jsop_andor(JSOp op);
     bool jsop_dup2();
     bool jsop_loophead(jsbytecode* pc);
     bool jsop_compare(JSOp op);
+    bool jsop_compare(JSOp op, MDefinition* left, MDefinition* right);
     bool getStaticName(JSObject* staticObject, PropertyName* name, bool* psucceeded,
                        MDefinition* lexicalCheck = nullptr);
     bool setStaticName(JSObject* staticObject, PropertyName* name);
     bool jsop_getgname(PropertyName* name);
     bool jsop_getname(PropertyName* name);
     bool jsop_intrinsic(PropertyName* name);
     bool jsop_bindname(PropertyName* name);
     bool jsop_getelem();
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -339,23 +339,23 @@ MInstruction::moveResumePointAsEntry()
 void
 MInstruction::clearResumePoint()
 {
     resumePoint_->resetInstruction();
     block()->discardPreAllocatedResumePoint(resumePoint_);
     resumePoint_ = nullptr;
 }
 
-static bool
-MaybeEmulatesUndefined(CompilerConstraintList* constraints, MDefinition* op)
-{
-    if (!op->mightBeType(MIRType_Object))
+bool
+MDefinition::maybeEmulatesUndefined(CompilerConstraintList* constraints)
+{
+    if (!mightBeType(MIRType_Object))
         return false;
 
-    TemporaryTypeSet* types = op->resultTypeSet();
+    TemporaryTypeSet* types = resultTypeSet();
     if (!types)
         return true;
 
     return types->maybeEmulatesUndefined(constraints);
 }
 
 static bool
 MaybeCallable(CompilerConstraintList* constraints, MDefinition* op)
@@ -376,18 +376,18 @@ MTest::New(TempAllocator& alloc, MDefini
     return new(alloc) MTest(ins, ifTrue, ifFalse);
 }
 
 void
 MTest::cacheOperandMightEmulateUndefined(CompilerConstraintList* constraints)
 {
     MOZ_ASSERT(operandMightEmulateUndefined());
 
-    if (!MaybeEmulatesUndefined(constraints, getOperand(0)))
-        markOperandCantEmulateUndefined();
+    if (!getOperand(0)->maybeEmulatesUndefined(constraints))
+        markNoOperandEmulatesUndefined();
 }
 
 MDefinition*
 MTest::foldsTo(TempAllocator& alloc)
 {
     MDefinition* op = getOperand(0);
 
     if (op->isNot()) {
@@ -2697,76 +2697,16 @@ SimpleArithOperand(MDefinition* op)
 static bool
 SafelyCoercesToDouble(MDefinition* op)
 {
     // Strings and symbols are unhandled -- visitToDouble() doesn't support them yet.
     // Null is unhandled -- ToDouble(null) == 0, but (0 == null) is false.
     return SimpleArithOperand(op) && !op->mightBeType(MIRType_Null);
 }
 
-static bool
-ObjectOrSimplePrimitive(MDefinition* op)
-{
-    // Return true if op is either undefined/null/boolean/int32 or an object.
-    return !op->mightBeType(MIRType_String)
-        && !op->mightBeType(MIRType_Symbol)
-        && !op->mightBeType(MIRType_Double)
-        && !op->mightBeType(MIRType_Float32)
-        && !op->mightBeType(MIRType_MagicOptimizedArguments)
-        && !op->mightBeType(MIRType_MagicHole)
-        && !op->mightBeType(MIRType_MagicIsConstructing);
-}
-
-static bool
-CanDoValueBitwiseCmp(CompilerConstraintList* constraints,
-                     MDefinition* lhs, MDefinition* rhs, bool looseEq)
-{
-    // Only primitive (not double/string) or objects are supported.
-    // I.e. Undefined/Null/Boolean/Int32 and Object
-    if (!ObjectOrSimplePrimitive(lhs) || !ObjectOrSimplePrimitive(rhs))
-        return false;
-
-    // Objects that emulate undefined are not supported.
-    if (MaybeEmulatesUndefined(constraints, lhs) || MaybeEmulatesUndefined(constraints, rhs))
-        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->mightBeType(MIRType_Undefined) && rhs->mightBeType(MIRType_Null)) ||
-            (lhs->mightBeType(MIRType_Null) && rhs->mightBeType(MIRType_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->mightBeType(MIRType_Int32) && rhs->mightBeType(MIRType_Boolean)) ||
-            (lhs->mightBeType(MIRType_Boolean) && rhs->mightBeType(MIRType_Int32)))
-        {
-            return false;
-        }
-
-        // For loosy comparison of an object with a Boolean/Number/String
-        // the valueOf the object is taken. Therefore not supported.
-        bool simpleLHS = lhs->mightBeType(MIRType_Boolean) || lhs->mightBeType(MIRType_Int32);
-        bool simpleRHS = rhs->mightBeType(MIRType_Boolean) || rhs->mightBeType(MIRType_Int32);
-        if ((lhs->mightBeType(MIRType_Object) && simpleRHS) ||
-            (rhs->mightBeType(MIRType_Object) && simpleLHS))
-        {
-            return false;
-        }
-    }
-
-    return true;
-}
-
 MIRType
 MCompare::inputType()
 {
     switch(compareType_) {
       case Compare_Undefined:
         return MIRType_Undefined;
       case Compare_Null:
         return MIRType_Null;
@@ -2805,171 +2745,143 @@ MustBeUInt32(MDefinition* def, MDefiniti
         MDefinition* rhs = def->toUrsh()->getOperand(1);
         return !def->toUrsh()->bailoutsDisabled()
             && rhs->isConstantValue()
             && rhs->constantValue().isInt32()
             && rhs->constantValue().toInt32() == 0;
     }
 
     if (def->isConstantValue()) {
+        if (def->isBox())
+            def = def->toBox()->getOperand(0);
         *pwrapped = def;
         return def->constantValue().isInt32()
             && def->constantValue().toInt32() >= 0;
     }
 
     return false;
 }
 
+/* static */ bool
+MBinaryInstruction::unsignedOperands(MDefinition* left, MDefinition* right)
+{
+    MDefinition* replace;
+    if (!MustBeUInt32(left, &replace))
+        return false;
+    if (replace->type() != MIRType_Int32)
+        return false;
+    if (!MustBeUInt32(right, &replace))
+        return false;
+    if (replace->type() != MIRType_Int32)
+        return false;
+    return true;
+}
+
 bool
-MBinaryInstruction::tryUseUnsignedOperands()
-{
-    MDefinition* newlhs;
-    MDefinition* newrhs;
-    if (MustBeUInt32(getOperand(0), &newlhs) && MustBeUInt32(getOperand(1), &newrhs)) {
-        if (newlhs->type() != MIRType_Int32 || newrhs->type() != MIRType_Int32)
-            return false;
-        if (newlhs != getOperand(0)) {
-            getOperand(0)->setImplicitlyUsedUnchecked();
-            replaceOperand(0, newlhs);
-        }
-        if (newrhs != getOperand(1)) {
-            getOperand(1)->setImplicitlyUsedUnchecked();
-            replaceOperand(1, newrhs);
-        }
-        return true;
-    }
-    return false;
+MBinaryInstruction::unsignedOperands()
+{
+    return unsignedOperands(getOperand(0), getOperand(1));
 }
 
 void
-MCompare::infer(CompilerConstraintList* constraints, BaselineInspector* inspector, jsbytecode* pc)
-{
-    MOZ_ASSERT(operandMightEmulateUndefined());
-
-    if (!MaybeEmulatesUndefined(constraints, getOperand(0)) &&
-        !MaybeEmulatesUndefined(constraints, getOperand(1)))
-    {
-        markNoOperandEmulatesUndefined();
+MBinaryInstruction::replaceWithUnsignedOperands()
+{
+    MOZ_ASSERT(unsignedOperands());
+
+    for (size_t i = 0; i < numOperands(); i++) {
+        MDefinition* replace;
+        MustBeUInt32(getOperand(i), &replace);
+        if (replace == getOperand(i))
+            continue;
+
+        getOperand(i)->setImplicitlyUsedUnchecked();
+        replaceOperand(i, replace);
     }
-
-    MIRType lhs = getOperand(0)->type();
-    MIRType rhs = getOperand(1)->type();
-
-    bool looseEq = jsop() == JSOP_EQ || jsop() == JSOP_NE;
-    bool strictEq = jsop() == JSOP_STRICTEQ || jsop() == JSOP_STRICTNE;
+}
+
+MCompare::CompareType
+MCompare::determineCompareType(JSOp op, MDefinition* left, MDefinition* right)
+{
+    MIRType lhs = left->type();
+    MIRType rhs = right->type();
+
+    bool looseEq = op == JSOP_EQ || op == JSOP_NE;
+    bool strictEq = op == JSOP_STRICTEQ || op == JSOP_STRICTNE;
     bool relationalEq = !(looseEq || strictEq);
 
     // Comparisons on unsigned integers may be treated as UInt32.
-    if (tryUseUnsignedOperands()) {
-        compareType_ = Compare_UInt32;
-        return;
-    }
+    if (unsignedOperands(left, right))
+        return Compare_UInt32;
 
     // 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))
     {
-        compareType_ = Compare_Int32MaybeCoerceBoth;
-        return;
+        return Compare_Int32MaybeCoerceBoth;
     }
 
     // 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))
     {
-        compareType_ = Compare_Int32MaybeCoerceBoth;
-        return;
+        return Compare_Int32MaybeCoerceBoth;
     }
 
     // Numeric comparisons against a double coerce to double.
-    if (IsNumberType(lhs) && IsNumberType(rhs)) {
-        compareType_ = Compare_Double;
-        return;
-    }
+    if (IsNumberType(lhs) && IsNumberType(rhs))
+        return Compare_Double;
 
     // Any comparison is allowed except strict eq.
-    if (!strictEq && IsFloatingPointType(lhs) && SafelyCoercesToDouble(getOperand(1))) {
-        compareType_ = Compare_DoubleMaybeCoerceRHS;
-        return;
-    }
-    if (!strictEq && IsFloatingPointType(rhs) && SafelyCoercesToDouble(getOperand(0))) {
-        compareType_ = Compare_DoubleMaybeCoerceLHS;
-        return;
-    }
+    if (!strictEq && IsFloatingPointType(rhs) && SafelyCoercesToDouble(left))
+        return Compare_DoubleMaybeCoerceLHS;
+    if (!strictEq && IsFloatingPointType(lhs) && SafelyCoercesToDouble(right))
+        return Compare_DoubleMaybeCoerceRHS;
 
     // Handle object comparison.
-    if (!relationalEq && lhs == MIRType_Object && rhs == MIRType_Object) {
-        compareType_ = Compare_Object;
-        return;
-    }
+    if (!relationalEq && lhs == MIRType_Object && rhs == MIRType_Object)
+        return Compare_Object;
 
     // Handle string comparisons. (Relational string compares are still unsupported).
-    if (!relationalEq && lhs == MIRType_String && rhs == MIRType_String) {
-        compareType_ = Compare_String;
-        return;
-    }
-
-    if (strictEq && lhs == MIRType_String) {
-        // Lowering expects the rhs to be definitly string.
-        compareType_ = Compare_StrictString;
-        swapOperands();
-        return;
-    }
-
-    if (strictEq && rhs == MIRType_String) {
-        compareType_ = Compare_StrictString;
-        return;
-    }
-
-    // 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;
-    }
+    if (!relationalEq && lhs == MIRType_String && rhs == MIRType_String)
+        return Compare_String;
+
+    // Handle strict string compare.
+    if (strictEq && lhs == MIRType_String)
+        return Compare_StrictString;
+    if (strictEq && rhs == MIRType_String)
+        return Compare_StrictString;
+
+    // Handle compare with lhs or rhs being Undefined or Null.
+    if (!relationalEq && IsNullOrUndefined(lhs))
+        return (lhs == MIRType_Null) ? Compare_Null : Compare_Undefined;
+    if (!relationalEq && IsNullOrUndefined(rhs))
+        return (rhs == MIRType_Null) ? Compare_Null : Compare_Undefined;
 
     // 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.
         MOZ_ASSERT(!(lhs == MIRType_Boolean && rhs == MIRType_Boolean));
-
-        // 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;
+        return Compare_Boolean;
     }
 
-    // Determine if we can do the compare based on a quick value check.
-    if (!relationalEq && CanDoValueBitwiseCmp(constraints, getOperand(0), getOperand(1), looseEq)) {
-        compareType_ = Compare_Value;
+    return Compare_Unknown;
+}
+
+void
+MCompare::cacheOperandMightEmulateUndefined(CompilerConstraintList* constraints)
+{
+    MOZ_ASSERT(operandMightEmulateUndefined());
+
+    if (getOperand(0)->maybeEmulatesUndefined(constraints))
         return;
-    }
-
-    // Type information is not good enough to pick out a particular type of
-    // comparison we can do here. Try to specialize based on any baseline
-    // caches that have been generated for the opcode. These will cause the
-    // instruction's type policy to insert fallible unboxes to the appropriate
-    // input types.
-
-    if (!strictEq)
-        compareType_ = inspector->expectedCompareType(pc);
+    if (getOperand(1)->maybeEmulatesUndefined(constraints))
+        return;
+
+    markNoOperandEmulatesUndefined();
 }
 
 MBitNot*
 MBitNot::New(TempAllocator& alloc, MDefinition* input)
 {
     return new(alloc) MBitNot(input);
 }
 
@@ -3048,17 +2960,17 @@ MTypeOf::foldsTo(TempAllocator& alloc)
     return MConstant::New(alloc, StringValue(TypeName(type, GetJitContext()->runtime->names())));
 }
 
 void
 MTypeOf::cacheInputMaybeCallableOrEmulatesUndefined(CompilerConstraintList* constraints)
 {
     MOZ_ASSERT(inputMaybeCallableOrEmulatesUndefined());
 
-    if (!MaybeEmulatesUndefined(constraints, input()) && !MaybeCallable(constraints, input()))
+    if (!input()->maybeEmulatesUndefined(constraints) && !MaybeCallable(constraints, input()))
         markInputNotCallableOrEmulatesUndefined();
 }
 
 MBitAnd*
 MBitAnd::New(TempAllocator& alloc, MDefinition* left, MDefinition* right)
 {
     return new(alloc) MBitAnd(left, right);
 }
@@ -3869,18 +3781,18 @@ MCompare::filtersUndefinedOrNull(bool tr
     *subject = lhs();
 }
 
 void
 MNot::cacheOperandMightEmulateUndefined(CompilerConstraintList* constraints)
 {
     MOZ_ASSERT(operandMightEmulateUndefined());
 
-    if (!MaybeEmulatesUndefined(constraints, getOperand(0)))
-        markOperandCantEmulateUndefined();
+    if (!getOperand(0)->maybeEmulatesUndefined(constraints))
+        markNoOperandEmulatesUndefined();
 }
 
 MDefinition*
 MNot::foldsTo(TempAllocator& alloc)
 {
     // Fold if the input is constant
     if (input()->isConstantValue() && !input()->constantValue().isMagic()) {
         bool result = input()->constantToBoolean();
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -648,16 +648,18 @@ class MDefinition : public MNode
         if (this->type() == MIRType_Value)
             return !resultTypeSet() || resultTypeSet()->mightBeMIRType(type);
 
         return false;
     }
 
     bool mightBeMagicType() const;
 
+    bool maybeEmulatesUndefined(CompilerConstraintList* constraints);
+
     // 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; }
     virtual bool canConsumeFloat32(MUse* use) const { return false; }
     virtual void trySpecializeFloat32(TempAllocator& alloc) {}
 #ifdef DEBUG
     // Used during the pass that checks that Float32 flow into valid MDefinitions
@@ -822,16 +824,17 @@ class MDefinition : public MNode
     }
     virtual AliasSet getAliasSet() const {
         // Instructions are effectful by default.
         return AliasSet::Store(AliasSet::Any);
     }
     bool isEffectful() const {
         return getAliasSet().isStore();
     }
+
 #ifdef DEBUG
     virtual bool needsResumePoint() const {
         // Return whether this instruction should have its own resume point.
         return isEffectful();
     }
 #endif
     virtual bool mightAlias(const MDefinition* store) const {
         // Return whether this load may depend on the specified store, given
@@ -1049,31 +1052,30 @@ class MBinaryInstruction : public MAryIn
 
   public:
     MDefinition* lhs() const {
         return getOperand(0);
     }
     MDefinition* rhs() const {
         return getOperand(1);
     }
+    void swapOperands() {
+        MDefinition* temp = getOperand(0);
+        replaceOperand(0, getOperand(1));
+        replaceOperand(1, temp);
+    }
 
   protected:
     HashNumber valueHash() const
     {
         MDefinition* lhs = getOperand(0);
         MDefinition* rhs = getOperand(1);
 
         return op() + lhs->id() + rhs->id();
     }
-    void swapOperands() {
-        MDefinition* temp = getOperand(0);
-        replaceOperand(0, getOperand(1));
-        replaceOperand(1, temp);
-    }
-
     bool binaryCongruentTo(const MDefinition* ins) const
     {
         if (op() != ins->op())
             return false;
 
         if (type() != ins->type())
             return false;
 
@@ -1098,20 +1100,24 @@ class MBinaryInstruction : public MAryIn
             insRight = insLeft;
             insLeft = tmp;
         }
 
         return left == insLeft &&
                right == insRight;
     }
 
-    // Return true if the operands to this instruction are both unsigned,
-    // in which case any wrapping operands were replaced with the underlying
-    // int32 operands.
-    bool tryUseUnsignedOperands();
+  public:
+    // Return if the operands to this instruction are both unsigned.
+    static bool unsignedOperands(MDefinition* left, MDefinition* right);
+    bool unsignedOperands();
+
+    // Replace any wrapping operands with the underlying int32 operands
+    // in case of unsigned operands.
+    void replaceWithUnsignedOperands();
 };
 
 class MTernaryInstruction : public MAryInstruction<3>
 {
   protected:
     MTernaryInstruction(MDefinition* first, MDefinition* second, MDefinition* third)
     {
         initOperand(0, first);
@@ -2732,17 +2738,17 @@ class MTest
     // background threads.  So make callers explicitly call it if they want us
     // to check whether the operand might do this.  If this method is never
     // called, we'll assume our operand can emulate undefined.
     void cacheOperandMightEmulateUndefined(CompilerConstraintList* constraints);
     MDefinition* foldsTo(TempAllocator& alloc) override;
     void filtersUndefinedOrNull(bool trueBranch, MDefinition** subject, bool* filtersUndefined,
                                 bool* filtersNull);
 
-    void markOperandCantEmulateUndefined() {
+    void markNoOperandEmulatesUndefined() {
         operandMightEmulateUndefined_ = false;
     }
     bool operandMightEmulateUndefined() const {
         return operandMightEmulateUndefined_;
     }
 #ifdef DEBUG
     bool isConsistentFloat32Use(MUse* use) const override {
         return true;
@@ -4200,18 +4206,16 @@ class MCompare
                               CompareType compareType);
 
     bool tryFold(bool* result);
     bool evaluateConstantOperands(TempAllocator& alloc, bool* result);
     MDefinition* foldsTo(TempAllocator& alloc) override;
     void filtersUndefinedOrNull(bool trueBranch, MDefinition** subject, bool* filtersUndefined,
                                 bool* filtersNull);
 
-    void infer(CompilerConstraintList* constraints,
-               BaselineInspector* inspector, jsbytecode* pc);
     CompareType compareType() const {
         return compareType_;
     }
     bool isInt32Comparison() const {
         return compareType() == Compare_Int32 ||
                compareType() == Compare_Int32MaybeCoerceBoth ||
                compareType() == Compare_Int32MaybeCoerceLHS ||
                compareType() == Compare_Int32MaybeCoerceRHS;
@@ -4260,16 +4264,19 @@ class MCompare
     void collectRangeInfoPreTrunc() override;
 
     void trySpecializeFloat32(TempAllocator& alloc) override;
     bool isFloat32Commutative() const override { return true; }
     bool needTruncation(TruncateKind kind) override;
     void truncate() override;
     TruncateKind operandTruncateKind(size_t index) const override;
 
+    static CompareType determineCompareType(JSOp op, MDefinition* left, MDefinition* right);
+    void cacheOperandMightEmulateUndefined(CompilerConstraintList* constraints);
+
 # ifdef DEBUG
     bool isConsistentFloat32Use(MUse* use) const override {
         // Both sides of the compare can be Float32
         return compareType_ == Compare_Float32;
     }
 # endif
 
     ALLOW_CLONE(MCompare)
@@ -7071,16 +7078,17 @@ class MOsrReturnValue
         return getOperand(0)->toOsrEntry();
     }
 };
 
 class MBinarySharedStub
   : public MBinaryInstruction,
     public MixPolicy<BoxPolicy<0>, BoxPolicy<1> >::Data
 {
+  protected:
     explicit MBinarySharedStub(MDefinition* left, MDefinition* right)
       : MBinaryInstruction(left, right)
     {
         setResultType(MIRType_Value);
     }
 
   public:
     INSTRUCTION_HEADER(BinarySharedStub)
@@ -8380,17 +8388,17 @@ class MNot
         ins->setResultType(MIRType_Int32);
         return ins;
     }
 
     INSTRUCTION_HEADER(Not)
 
     MDefinition* foldsTo(TempAllocator& alloc) override;
 
-    void markOperandCantEmulateUndefined() {
+    void markNoOperandEmulatesUndefined() {
         operandMightEmulateUndefined_ = false;
     }
     bool operandMightEmulateUndefined() const {
         return operandMightEmulateUndefined_;
     }
     bool operandIsNeverNaN() const {
         return operandIsNeverNaN_;
     }
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -2485,18 +2485,20 @@ void
 MDiv::truncate()
 {
     MOZ_ASSERT(needTruncation(truncateKind()));
     specialization_ = MIRType_Int32;
     setResultType(MIRType_Int32);
 
     // Divisions where the lhs and rhs are unsigned and the result is
     // truncated can be lowered more efficiently.
-    if (tryUseUnsignedOperands())
+    if (unsignedOperands()) {
+        replaceWithUnsignedOperands();
         unsigned_ = true;
+    }
 }
 
 bool
 MMod::needTruncation(TruncateKind kind)
 {
     // Remember analysis, needed for fallible checks.
     setTruncateKind(kind);
 
@@ -2506,18 +2508,20 @@ MMod::needTruncation(TruncateKind kind)
 void
 MMod::truncate()
 {
     // As for division, handle unsigned modulus with a truncated result.
     MOZ_ASSERT(needTruncation(truncateKind()));
     specialization_ = MIRType_Int32;
     setResultType(MIRType_Int32);
 
-    if (tryUseUnsignedOperands())
+    if (unsignedOperands()) {
+        replaceWithUnsignedOperands();
         unsigned_ = true;
+    }
 }
 
 bool
 MToDouble::needTruncation(TruncateKind kind)
 {
     MOZ_ASSERT(type() == MIRType_Double);
     setTruncateKind(kind);
 
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -1570,20 +1570,23 @@ ICUnaryArith_Double::Compiler::generateS
 //
 // Compare_Fallback
 //
 
 static bool
 DoCompareFallback(JSContext* cx, BaselineFrame* frame, ICCompare_Fallback* stub_, HandleValue lhs,
                   HandleValue rhs, MutableHandleValue ret)
 {
+    ICStubCompiler::Engine engine = SharedStubEngine(frame);
+    RootedScript script(cx, SharedStubScript(frame, stub_));
+
     // This fallback stub may trigger debug mode toggling.
-    DebugModeOSRVolatileStub<ICCompare_Fallback*> stub(frame, stub_);
+    DebugModeOSRVolatileStub<ICCompare_Fallback*> stub(engine, frame, stub_);
 
-    jsbytecode* pc = stub->icEntry()->pc(frame->script());
+    jsbytecode* pc = stub->icEntry()->pc(script);
     JSOp op = JSOp(*pc);
 
     FallbackICSpew(cx, stub, "Compare(%s)", js_CodeName[op]);
 
     // Case operations in a CONDSWITCH are performing strict equality.
     if (op == JSOP_CASE)
         op = JSOP_STRICTEQ;
 
@@ -1640,22 +1643,20 @@ DoCompareFallback(JSContext* cx, Baselin
 
     // Check to see if a new stub should be generated.
     if (stub->numOptimizedStubs() >= ICCompare_Fallback::MAX_OPTIMIZED_STUBS) {
         // TODO: Discard all stubs in this IC and replace with inert megamorphic stub.
         // But for now we just bail.
         return true;
     }
 
-    JSScript* script = frame->script();
-
     // Try to generate new stubs.
     if (lhs.isInt32() && rhs.isInt32()) {
         JitSpew(JitSpew_BaselineIC, "  Generating %s(Int32, Int32) stub", js_CodeName[op]);
-        ICCompare_Int32::Compiler compiler(cx, op);
+        ICCompare_Int32::Compiler compiler(cx, op, engine);
         ICStub* int32Stub = compiler.getStub(compiler.getStubSpace(script));
         if (!int32Stub)
             return false;
 
         stub->addNewStub(int32Stub);
         return true;
     }
 
@@ -1663,97 +1664,97 @@ DoCompareFallback(JSContext* cx, Baselin
         return true;
 
     if (lhs.isNumber() && rhs.isNumber()) {
         JitSpew(JitSpew_BaselineIC, "  Generating %s(Number, Number) stub", js_CodeName[op]);
 
         // Unlink int32 stubs, it's faster to always use the double stub.
         stub->unlinkStubsWithKind(cx, ICStub::Compare_Int32);
 
-        ICCompare_Double::Compiler compiler(cx, op);
+        ICCompare_Double::Compiler compiler(cx, op, engine);
         ICStub* doubleStub = compiler.getStub(compiler.getStubSpace(script));
         if (!doubleStub)
             return false;
 
         stub->addNewStub(doubleStub);
         return true;
     }
 
     if ((lhs.isNumber() && rhs.isUndefined()) ||
         (lhs.isUndefined() && rhs.isNumber()))
     {
         JitSpew(JitSpew_BaselineIC, "  Generating %s(%s, %s) stub", js_CodeName[op],
                     rhs.isUndefined() ? "Number" : "Undefined",
                     rhs.isUndefined() ? "Undefined" : "Number");
-        ICCompare_NumberWithUndefined::Compiler compiler(cx, op, lhs.isUndefined());
+        ICCompare_NumberWithUndefined::Compiler compiler(cx, op, engine, lhs.isUndefined());
         ICStub* doubleStub = compiler.getStub(compiler.getStubSpace(script));
         if (!doubleStub)
             return false;
 
         stub->addNewStub(doubleStub);
         return true;
     }
 
     if (lhs.isBoolean() && rhs.isBoolean()) {
         JitSpew(JitSpew_BaselineIC, "  Generating %s(Boolean, Boolean) stub", js_CodeName[op]);
-        ICCompare_Boolean::Compiler compiler(cx, op);
+        ICCompare_Boolean::Compiler compiler(cx, op, engine);
         ICStub* booleanStub = compiler.getStub(compiler.getStubSpace(script));
         if (!booleanStub)
             return false;
 
         stub->addNewStub(booleanStub);
         return true;
     }
 
     if ((lhs.isBoolean() && rhs.isInt32()) || (lhs.isInt32() && rhs.isBoolean())) {
         JitSpew(JitSpew_BaselineIC, "  Generating %s(%s, %s) stub", js_CodeName[op],
                     rhs.isInt32() ? "Boolean" : "Int32",
                     rhs.isInt32() ? "Int32" : "Boolean");
-        ICCompare_Int32WithBoolean::Compiler compiler(cx, op, lhs.isInt32());
+        ICCompare_Int32WithBoolean::Compiler compiler(cx, op, engine, lhs.isInt32());
         ICStub* optStub = compiler.getStub(compiler.getStubSpace(script));
         if (!optStub)
             return false;
 
         stub->addNewStub(optStub);
         return true;
     }
 
     if (IsEqualityOp(op)) {
         if (lhs.isString() && rhs.isString() && !stub->hasStub(ICStub::Compare_String)) {
             JitSpew(JitSpew_BaselineIC, "  Generating %s(String, String) stub", js_CodeName[op]);
-            ICCompare_String::Compiler compiler(cx, op);
+            ICCompare_String::Compiler compiler(cx, op, engine);
             ICStub* stringStub = compiler.getStub(compiler.getStubSpace(script));
             if (!stringStub)
                 return false;
 
             stub->addNewStub(stringStub);
             return true;
         }
 
         if (lhs.isObject() && rhs.isObject()) {
             MOZ_ASSERT(!stub->hasStub(ICStub::Compare_Object));
             JitSpew(JitSpew_BaselineIC, "  Generating %s(Object, Object) stub", js_CodeName[op]);
-            ICCompare_Object::Compiler compiler(cx, op);
+            ICCompare_Object::Compiler compiler(cx, op, engine);
             ICStub* objectStub = compiler.getStub(compiler.getStubSpace(script));
             if (!objectStub)
                 return false;
 
             stub->addNewStub(objectStub);
             return true;
         }
 
         if ((lhs.isObject() || lhs.isNull() || lhs.isUndefined()) &&
             (rhs.isObject() || rhs.isNull() || rhs.isUndefined()) &&
             !stub->hasStub(ICStub::Compare_ObjectWithUndefined))
         {
             JitSpew(JitSpew_BaselineIC, "  Generating %s(Obj/Null/Undef, Obj/Null/Undef) stub",
                     js_CodeName[op]);
             bool lhsIsUndefined = lhs.isNull() || lhs.isUndefined();
             bool compareWithNull = lhs.isNull() || rhs.isNull();
-            ICCompare_ObjectWithUndefined::Compiler compiler(cx, op,
+            ICCompare_ObjectWithUndefined::Compiler compiler(cx, op, engine,
                                                              lhsIsUndefined, compareWithNull);
             ICStub* objectStub = compiler.getStub(compiler.getStubSpace(script));
             if (!objectStub)
                 return false;
 
             stub->addNewStub(objectStub);
             return true;
         }
@@ -1767,18 +1768,16 @@ DoCompareFallback(JSContext* cx, Baselin
 typedef bool (*DoCompareFallbackFn)(JSContext*, BaselineFrame*, ICCompare_Fallback*,
                                     HandleValue, HandleValue, MutableHandleValue);
 static const VMFunction DoCompareFallbackInfo =
     FunctionInfo<DoCompareFallbackFn>(DoCompareFallback, TailCall, PopValues(2));
 
 bool
 ICCompare_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
 {
-    MOZ_ASSERT(engine_ == Engine::Baseline);
-
     MOZ_ASSERT(R0 == JSReturnOperand);
 
     // Restore the tail call register.
     EmitRestoreTailCallReg(masm);
 
     // Ensure stack is fully synced for the expression decompiler.
     masm.pushValue(R0);
     masm.pushValue(R1);
@@ -1793,18 +1792,16 @@ ICCompare_Fallback::Compiler::generateSt
 
 //
 // Compare_String
 //
 
 bool
 ICCompare_String::Compiler::generateStubCode(MacroAssembler& masm)
 {
-    MOZ_ASSERT(engine_ == Engine::Baseline);
-
     Label failure;
     masm.branchTestString(Assembler::NotEqual, R0, &failure);
     masm.branchTestString(Assembler::NotEqual, R1, &failure);
 
     MOZ_ASSERT(IsEqualityOp(op));
 
     Register left = masm.extractString(R0, ExtractTemp0);
     Register right = masm.extractString(R1, ExtractTemp1);
@@ -1823,18 +1820,16 @@ ICCompare_String::Compiler::generateStub
 
 //
 // Compare_Boolean
 //
 
 bool
 ICCompare_Boolean::Compiler::generateStubCode(MacroAssembler& masm)
 {
-    MOZ_ASSERT(engine_ == Engine::Baseline);
-
     Label failure;
     masm.branchTestBoolean(Assembler::NotEqual, R0, &failure);
     masm.branchTestBoolean(Assembler::NotEqual, R1, &failure);
 
     Register left = masm.extractInt32(R0, ExtractTemp0);
     Register right = masm.extractInt32(R1, ExtractTemp1);
 
     // Compare payload regs of R0 and R1.
@@ -1853,18 +1848,16 @@ ICCompare_Boolean::Compiler::generateStu
 
 //
 // Compare_NumberWithUndefined
 //
 
 bool
 ICCompare_NumberWithUndefined::Compiler::generateStubCode(MacroAssembler& masm)
 {
-    MOZ_ASSERT(engine_ == Engine::Baseline);
-
     ValueOperand numberOperand, undefinedOperand;
     if (lhsIsUndefined) {
         numberOperand = R1;
         undefinedOperand = R0;
     } else {
         numberOperand = R0;
         undefinedOperand = R1;
     }
@@ -1887,18 +1880,16 @@ ICCompare_NumberWithUndefined::Compiler:
 
 //
 // Compare_Object
 //
 
 bool
 ICCompare_Object::Compiler::generateStubCode(MacroAssembler& masm)
 {
-    MOZ_ASSERT(engine_ == Engine::Baseline);
-
     Label failure;
     masm.branchTestObject(Assembler::NotEqual, R0, &failure);
     masm.branchTestObject(Assembler::NotEqual, R1, &failure);
 
     MOZ_ASSERT(IsEqualityOp(op));
 
     Register left = masm.extractObject(R0, ExtractTemp0);
     Register right = masm.extractObject(R1, ExtractTemp1);
@@ -1921,17 +1912,16 @@ ICCompare_Object::Compiler::generateStub
 
 //
 // Compare_ObjectWithUndefined
 //
 
 bool
 ICCompare_ObjectWithUndefined::Compiler::generateStubCode(MacroAssembler& masm)
 {
-    MOZ_ASSERT(engine_ == Engine::Baseline);
     MOZ_ASSERT(IsEqualityOp(op));
 
     ValueOperand objectOperand, undefinedOperand;
     if (lhsIsUndefined) {
         objectOperand = R1;
         undefinedOperand = R0;
     } else {
         objectOperand = R0;
@@ -1987,18 +1977,16 @@ ICCompare_ObjectWithUndefined::Compiler:
 
 //
 // Compare_Int32WithBoolean
 //
 
 bool
 ICCompare_Int32WithBoolean::Compiler::generateStubCode(MacroAssembler& masm)
 {
-    MOZ_ASSERT(engine_ == Engine::Baseline);
-
     Label failure;
     ValueOperand int32Val;
     ValueOperand boolVal;
     if (lhsIsInt32_) {
         int32Val = R0;
         boolVal = R1;
     } else {
         boolVal = R0;
--- a/js/src/jit/SharedIC.h
+++ b/js/src/jit/SharedIC.h
@@ -1474,17 +1474,23 @@ class ICUnaryArith_Double : public ICStu
         ICStub* getStub(ICStubSpace* space) {
             return newStub<ICUnaryArith_Double>(space, getStubCode());
         }
     };
 };
 
 // Compare
 //      JSOP_LT
+//      JSOP_LE
 //      JSOP_GT
+//      JSOP_GE
+//      JSOP_EQ
+//      JSOP_NE
+//      JSOP_STRICTEQ
+//      JSOP_STRICTNE
 
 class ICCompare_Fallback : public ICFallbackStub
 {
     friend class ICStubSpace;
 
     explicit ICCompare_Fallback(JitCode* stubCode)
       : ICFallbackStub(ICStub::Compare_Fallback, stubCode) {}
 
@@ -1500,18 +1506,18 @@ class ICCompare_Fallback : public ICFall
     }
 
     // Compiler for this stub kind.
     class Compiler : public ICStubCompiler {
       protected:
         bool generateStubCode(MacroAssembler& masm);
 
       public:
-        explicit Compiler(JSContext* cx)
-          : ICStubCompiler(cx, ICStub::Compare_Fallback, Engine::Baseline) {}
+        explicit Compiler(JSContext* cx, Engine engine)
+          : ICStubCompiler(cx, ICStub::Compare_Fallback, engine) {}
 
         ICStub* getStub(ICStubSpace* space) {
             return newStub<ICCompare_Fallback>(space, getStubCode());
         }
     };
 };
 
 class ICCompare_Int32 : public ICStub
@@ -1523,18 +1529,18 @@ class ICCompare_Int32 : public ICStub
 
   public:
     // Compiler for this stub kind.
     class Compiler : public ICMultiStubCompiler {
       protected:
         bool generateStubCode(MacroAssembler& masm);
 
       public:
-        Compiler(JSContext* cx, JSOp op)
-          : ICMultiStubCompiler(cx, ICStub::Compare_Int32, op, Engine::Baseline) {}
+        Compiler(JSContext* cx, JSOp op, Engine engine)
+          : ICMultiStubCompiler(cx, ICStub::Compare_Int32, op, engine) {}
 
         ICStub* getStub(ICStubSpace* space) {
             return newStub<ICCompare_Int32>(space, getStubCode());
         }
     };
 };
 
 class ICCompare_Double : public ICStub
@@ -1546,18 +1552,18 @@ class ICCompare_Double : public ICStub
     {}
 
   public:
     class Compiler : public ICMultiStubCompiler {
       protected:
         bool generateStubCode(MacroAssembler& masm);
 
       public:
-        Compiler(JSContext* cx, JSOp op)
-          : ICMultiStubCompiler(cx, ICStub::Compare_Double, op, Engine::Baseline)
+        Compiler(JSContext* cx, JSOp op, Engine engine)
+          : ICMultiStubCompiler(cx, ICStub::Compare_Double, op, engine)
         {}
 
         ICStub* getStub(ICStubSpace* space) {
             return newStub<ICCompare_Double>(space, getStubCode());
         }
     };
 };
 
@@ -1578,18 +1584,18 @@ class ICCompare_NumberWithUndefined : pu
 
     class Compiler : public ICMultiStubCompiler {
       protected:
         bool generateStubCode(MacroAssembler& masm);
 
         bool lhsIsUndefined;
 
       public:
-        Compiler(JSContext* cx, JSOp op, bool lhsIsUndefined)
-          : ICMultiStubCompiler(cx, ICStub::Compare_NumberWithUndefined, op, Engine::Baseline),
+        Compiler(JSContext* cx, JSOp op, Engine engine, bool lhsIsUndefined)
+          : ICMultiStubCompiler(cx, ICStub::Compare_NumberWithUndefined, op, engine),
             lhsIsUndefined(lhsIsUndefined)
         {}
 
         virtual int32_t getKey() const {
             return static_cast<int32_t>(engine_) |
                   (static_cast<int32_t>(kind) << 1) |
                   (static_cast<int32_t>(op) << 17) |
                   (static_cast<int32_t>(lhsIsUndefined) << 25);
@@ -1611,18 +1617,18 @@ class ICCompare_String : public ICStub
     {}
 
   public:
     class Compiler : public ICMultiStubCompiler {
       protected:
         bool generateStubCode(MacroAssembler& masm);
 
       public:
-        Compiler(JSContext* cx, JSOp op)
-          : ICMultiStubCompiler(cx, ICStub::Compare_String, op, Engine::Baseline)
+        Compiler(JSContext* cx, JSOp op, Engine engine)
+          : ICMultiStubCompiler(cx, ICStub::Compare_String, op, engine)
         {}
 
         ICStub* getStub(ICStubSpace* space) {
             return newStub<ICCompare_String>(space, getStubCode());
         }
     };
 };
 
@@ -1635,18 +1641,18 @@ class ICCompare_Boolean : public ICStub
     {}
 
   public:
     class Compiler : public ICMultiStubCompiler {
       protected:
         bool generateStubCode(MacroAssembler& masm);
 
       public:
-        Compiler(JSContext* cx, JSOp op)
-          : ICMultiStubCompiler(cx, ICStub::Compare_Boolean, op, Engine::Baseline)
+        Compiler(JSContext* cx, JSOp op, Engine engine)
+          : ICMultiStubCompiler(cx, ICStub::Compare_Boolean, op, engine)
         {}
 
         ICStub* getStub(ICStubSpace* space) {
             return newStub<ICCompare_Boolean>(space, getStubCode());
         }
     };
 };
 
@@ -1659,18 +1665,18 @@ class ICCompare_Object : public ICStub
     {}
 
   public:
     class Compiler : public ICMultiStubCompiler {
       protected:
         bool generateStubCode(MacroAssembler& masm);
 
       public:
-        Compiler(JSContext* cx, JSOp op)
-          : ICMultiStubCompiler(cx, ICStub::Compare_Object, op, Engine::Baseline)
+        Compiler(JSContext* cx, JSOp op, Engine engine)
+          : ICMultiStubCompiler(cx, ICStub::Compare_Object, op, engine)
         {}
 
         ICStub* getStub(ICStubSpace* space) {
             return newStub<ICCompare_Object>(space, getStubCode());
         }
     };
 };
 
@@ -1686,18 +1692,18 @@ class ICCompare_ObjectWithUndefined : pu
     class Compiler : public ICMultiStubCompiler {
       protected:
         bool generateStubCode(MacroAssembler& masm);
 
         bool lhsIsUndefined;
         bool compareWithNull;
 
       public:
-        Compiler(JSContext* cx, JSOp op, bool lhsIsUndefined, bool compareWithNull)
-          : ICMultiStubCompiler(cx, ICStub::Compare_ObjectWithUndefined, op, Engine::Baseline),
+        Compiler(JSContext* cx, JSOp op, Engine engine, bool lhsIsUndefined, bool compareWithNull)
+          : ICMultiStubCompiler(cx, ICStub::Compare_ObjectWithUndefined, op, engine),
             lhsIsUndefined(lhsIsUndefined),
             compareWithNull(compareWithNull)
         {}
 
         virtual int32_t getKey() const {
             return static_cast<int32_t>(engine_) |
                   (static_cast<int32_t>(kind) << 1) |
                   (static_cast<int32_t>(op) << 17) |
@@ -1736,18 +1742,18 @@ class ICCompare_Int32WithBoolean : publi
         virtual int32_t getKey() const {
             return static_cast<int32_t>(engine_) |
                   (static_cast<int32_t>(kind) << 1) |
                   (static_cast<int32_t>(op_) << 17) |
                   (static_cast<int32_t>(lhsIsInt32_) << 25);
         }
 
       public:
-        Compiler(JSContext* cx, JSOp op, bool lhsIsInt32)
-          : ICStubCompiler(cx, ICStub::Compare_Int32WithBoolean, Engine::Baseline),
+        Compiler(JSContext* cx, JSOp op, Engine engine, bool lhsIsInt32)
+          : ICStubCompiler(cx, ICStub::Compare_Int32WithBoolean, engine),
             op_(op),
             lhsIsInt32_(lhsIsInt32)
         {}
 
         ICStub* getStub(ICStubSpace* space) {
             return newStub<ICCompare_Int32WithBoolean>(space, getStubCode(), lhsIsInt32_);
         }
     };