Bug 766218 - Make strict equality comparisons with known booleans faster. r=dvander
authorJan de Mooij <jdemooij@mozilla.com>
Fri, 29 Jun 2012 11:04:55 +0200
changeset 106488 82120910b08f651baf042f96225a3b608daeeae7
parent 106487 97e97b5b6db1b540aaca6f837083c771b5348c70
child 106489 13304da358b6f6e9a068674cff5c85d5a07404d2
push id23447
push userdanderson@mozilla.com
push dateTue, 11 Sep 2012 17:34:27 +0000
treeherdermozilla-central@fdfaef738a00 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdvander
bugs766218
milestone16.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 766218 - Make strict equality comparisons with known booleans faster. r=dvander
js/src/ion/CodeGenerator.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/arm/CodeGenerator-arm.cpp
js/src/ion/arm/CodeGenerator-arm.h
js/src/ion/shared/CodeGenerator-x86-shared.cpp
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/jit-test/tests/ion/bug766218.js
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -1402,17 +1402,17 @@ CodeGenerator::visitBinaryV(LBinaryV *li
         JS_NOT_REACHED("Unexpected binary op");
         return false;
     }
 }
 
 bool
 CodeGenerator::visitCompareS(LCompareS *lir)
 {
-    JSOp op = lir->jsop();
+    JSOp op = lir->mir()->jsop();
     Register left = ToRegister(lir->left());
     Register right = ToRegister(lir->right());
     Register output = ToRegister(lir->output());
 
     typedef bool (*pf)(JSContext *, HandleString, HandleString, JSBool *);
     static const VMFunction stringsEqualInfo = FunctionInfo<pf>(ion::StringsEqual<true>);
     static const VMFunction stringsNotEqualInfo = FunctionInfo<pf>(ion::StringsEqual<false>);
 
@@ -1464,17 +1464,17 @@ CodeGenerator::visitCompareV(LCompareV *
     static const VMFunction LtInfo = FunctionInfo<pf>(ion::LessThan);
     static const VMFunction LeInfo = FunctionInfo<pf>(ion::LessThanOrEqual);
     static const VMFunction GtInfo = FunctionInfo<pf>(ion::GreaterThan);
     static const VMFunction GeInfo = FunctionInfo<pf>(ion::GreaterThanOrEqual);
 
     pushArg(ToValue(lir, LBinaryV::RhsInput));
     pushArg(ToValue(lir, LBinaryV::LhsInput));
 
-    switch (lir->jsop()) {
+    switch (lir->mir()->jsop()) {
       case JSOP_EQ:
         return callVM(EqInfo, lir);
 
       case JSOP_NE:
         return callVM(NeInfo, lir);
 
       case JSOP_STRICTEQ:
         return callVM(StrictEqInfo, lir);
--- a/js/src/ion/LIR-Common.h
+++ b/js/src/ion/LIR-Common.h
@@ -735,104 +735,89 @@ class LCompare : public LInstructionHelp
     }
     MCompare *mir() {
         return mir_->toCompare();
     }
 };
 
 class LCompareD : public LInstructionHelper<1, 2, 0>
 {
-    JSOp jsop_;
-
   public:
     LIR_HEADER(CompareD);
-    LCompareD(JSOp jsop, const LAllocation &left, const LAllocation &right)
-      : jsop_(jsop)
-    {
+    LCompareD(const LAllocation &left, const LAllocation &right) {
         setOperand(0, left);
         setOperand(1, right);
     }
 
-    JSOp jsop() const {
-        return jsop_;
-    }
     const LAllocation *left() {
         return getOperand(0);
     }
     const LAllocation *right() {
         return getOperand(1);
     }
     const LDefinition *output() {
         return getDef(0);
     }
+    MCompare *mir() {
+        return mir_->toCompare();
+    }
 };
 
 class LCompareS : public LInstructionHelper<1, 2, 0>
 {
-    JSOp jsop_;
-
   public:
     LIR_HEADER(CompareS);
-    LCompareS(JSOp jsop, const LAllocation &left, const LAllocation &right)
-      : jsop_(jsop)
-    {
+    LCompareS(const LAllocation &left, const LAllocation &right) {
         setOperand(0, left);
         setOperand(1, right);
     }
 
-    JSOp jsop() const {
-        return jsop_;
-    }
     const LAllocation *left() {
         return getOperand(0);
     }
     const LAllocation *right() {
         return getOperand(1);
     }
     const LDefinition *output() {
         return getDef(0);
     }
+    MCompare *mir() {
+        return mir_->toCompare();
+    }
 };
 
 class LCompareV : public LCallInstructionHelper<1, 2 * BOX_PIECES, 0>
 {
-    JSOp jsop_;
-
   public:
     LIR_HEADER(CompareV);
 
-    LCompareV(JSOp jsop)
-      : jsop_(jsop)
-    { }
-
-    JSOp jsop() const {
-        return jsop_;
-    }
-
     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_;
 
   public:
     LIR_HEADER(CompareAndBranch);
-    LCompareAndBranch(MCompare *mir, JSOp jsop, const LAllocation &left, const LAllocation &right,
-                       MBasicBlock *ifTrue, MBasicBlock *ifFalse)
+    LCompareAndBranch(JSOp jsop, const LAllocation &left, const LAllocation &right,
+                      MBasicBlock *ifTrue, MBasicBlock *ifFalse)
       : jsop_(jsop),
         ifTrue_(ifTrue),
         ifFalse_(ifFalse)
     {
-        mir_ = mir;
         setOperand(0, left);
         setOperand(1, right);
     }
 
     JSOp jsop() const {
         return jsop_;
     }
     MBasicBlock *ifTrue() const {
@@ -849,47 +834,102 @@ class LCompareAndBranch : public LInstru
     }
     MCompare *mir() {
         return mir_->toCompare();
     }
 };
 
 class LCompareDAndBranch : public LInstructionHelper<0, 2, 0>
 {
-    JSOp jsop_;
     MBasicBlock *ifTrue_;
     MBasicBlock *ifFalse_;
 
   public:
     LIR_HEADER(CompareDAndBranch);
-    LCompareDAndBranch(JSOp jsop, const LAllocation &left, const LAllocation &right,
+    LCompareDAndBranch(const LAllocation &left, const LAllocation &right,
                        MBasicBlock *ifTrue, MBasicBlock *ifFalse)
-      : jsop_(jsop),
-        ifTrue_(ifTrue),
+      : ifTrue_(ifTrue),
         ifFalse_(ifFalse)
     {
         setOperand(0, left);
         setOperand(1, right);
     }
 
-    JSOp jsop() const {
-        return jsop_;
-    }
     MBasicBlock *ifTrue() const {
         return ifTrue_;
     }
     MBasicBlock *ifFalse() const {
         return ifFalse_;
     }
     const LAllocation *left() {
         return getOperand(0);
     }
     const LAllocation *right() {
         return getOperand(1);
     }
+    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);
+
+    LCompareB(const LAllocation &rhs) {
+        setOperand(BOX_PIECES, rhs);
+    }
+
+    static const size_t Lhs = 0;
+
+    const LAllocation *rhs() {
+        return getOperand(BOX_PIECES);
+    }
+
+    const LDefinition *output() {
+        return getDef(0);
+    }
+    MCompare *mir() {
+        return mir_->toCompare();
+    }
+};
+
+class LCompareBAndBranch : public LInstructionHelper<0, BOX_PIECES + 1, 0>
+{
+    MBasicBlock *ifTrue_;
+    MBasicBlock *ifFalse_;
+
+  public:
+    LIR_HEADER(CompareBAndBranch);
+
+    LCompareBAndBranch(const LAllocation &rhs, MBasicBlock *ifTrue, MBasicBlock *ifFalse)
+      : ifTrue_(ifTrue), ifFalse_(ifFalse)
+    {
+        setOperand(BOX_PIECES, rhs);
+    }
+
+    static const size_t Lhs = 0;
+
+    const LAllocation *rhs() {
+        return getOperand(BOX_PIECES);
+    }
+
+    MBasicBlock *ifTrue() const {
+        return ifTrue_;
+    }
+    MBasicBlock *ifFalse() const {
+        return ifFalse_;
+    }
+    MCompare *mir() {
+        return mir_->toCompare();
+    }
 };
 
 class LIsNullOrUndefined : public LInstructionHelper<1, BOX_PIECES, 0>
 {
   public:
     LIR_HEADER(IsNullOrUndefined);
 
     static const size_t Value = 0;
--- a/js/src/ion/LOpcodes.h
+++ b/js/src/ion/LOpcodes.h
@@ -81,16 +81,18 @@
     _(TestDAndBranch)               \
     _(TestVAndBranch)               \
     _(Compare)                      \
     _(CompareD)                     \
     _(CompareS)                     \
     _(CompareV)                     \
     _(CompareAndBranch)             \
     _(CompareDAndBranch)            \
+    _(CompareB)                     \
+    _(CompareBAndBranch)            \
     _(IsNullOrUndefined)            \
     _(IsNullOrUndefinedAndBranch)   \
     _(AbsI)                         \
     _(AbsD)                         \
     _(SqrtD)                        \
     _(MathFunctionD)                \
     _(NotI)                         \
     _(NotD)                         \
--- a/js/src/ion/Lowering.cpp
+++ b/js/src/ion/Lowering.cpp
@@ -321,47 +321,57 @@ LIRGenerator::visitTest(MTest *test)
 
     // Objects are easy, too.
     if (opd->type() == MIRType_Object)
         return add(new LGoto(ifTrue));
 
     // Check if the operand for this test is a compare operation. If it is, we want
     // to emit an LCompare*AndBranch rather than an LTest*AndBranch, to fuse the
     // compare and jump instructions.
-    if (opd->isCompare()) {
+    if (opd->isCompare() && opd->isEmittedAtUses()) {
         MCompare *comp = opd->toCompare();
-        MDefinition *left = comp->getOperand(0);
-        MDefinition *right = comp->getOperand(1);
+        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(opd->toCompare(), op, useRegister(left), rhs,
-                                             ifTrue, ifFalse));
+            return add(new LCompareAndBranch(op, useRegister(left), rhs, ifTrue, ifFalse), comp);
         }
         if (comp->specialization() == MIRType_Double) {
-            return add(new LCompareDAndBranch(comp->jsop(), useRegister(left),
-                                              useRegister(right), ifTrue, ifFalse));
+            return add(new LCompareDAndBranch(useRegister(left), useRegister(right), ifTrue,
+                                              ifFalse), comp);
         }
 
         // The second operand has known null/undefined type, so just test the
         // first operand.
         if (IsNullOrUndefined(comp->specialization())) {
             LIsNullOrUndefinedAndBranch *lir = new LIsNullOrUndefinedAndBranch(ifTrue, ifFalse);
             if (!useBox(lir, LIsNullOrUndefinedAndBranch::Value, left))
                 return false;
             return add(lir, comp);
         }
+
+        if (comp->specialization() == MIRType_Boolean) {
+            JS_ASSERT(left->type() == MIRType_Value);
+            JS_ASSERT(right->type() == MIRType_Boolean);
+
+            LCompareBAndBranch *lir = new LCompareBAndBranch(useRegisterOrConstant(right),
+                                                             ifTrue, ifFalse);
+            if (!useBox(lir, LCompareBAndBranch::Lhs, left))
+                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));
 }
@@ -384,30 +394,30 @@ CanEmitCompareAtUses(MInstruction *ins)
         foundTest = true;
     }
     return true;
 }
 
 bool
 LIRGenerator::visitCompare(MCompare *comp)
 {
-    MDefinition *left = comp->getOperand(0);
-    MDefinition *right = comp->getOperand(1);
+    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);
 
         // 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(comp->jsop(), useRegister(left), useRegister(right));
+            LCompareS *lir = new LCompareS(useRegister(left), useRegister(right));
             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 willl emit an LCompare*AndBranch instruction in place
         // of this compare and any test that uses this compare. Thus, we can
@@ -419,27 +429,37 @@ LIRGenerator::visitCompare(MCompare *com
             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(comp->jsop(), useRegister(left), useRegister(right)), comp);
+            return define(new LCompareD(useRegister(left), useRegister(right)), comp);
+
+        if (comp->specialization() == MIRType_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);
+        }
 
         JS_ASSERT(IsNullOrUndefined(comp->specialization()));
 
         LIsNullOrUndefined *lir = new LIsNullOrUndefined();
         if (!useBox(lir, LIsNullOrUndefined::Value, comp->getOperand(0)))
             return false;
         return define(lir, comp);
     }
 
-    LCompareV *lir = new LCompareV(comp->jsop());
+    LCompareV *lir = new LCompareV();
     if (!useBox(lir, LCompareV::LhsInput, left))
         return false;
     if (!useBox(lir, LCompareV::RhsInput, right))
         return false;
     return defineVMReturn(lir, comp) && assignSafepoint(lir, comp);
 }
 
 static void
--- a/js/src/ion/MIR.cpp
+++ b/js/src/ion/MIR.cpp
@@ -1008,16 +1008,33 @@ MCompare::infer(JSContext *cx, const Typ
             return;
         }
 
         if (IsNullOrUndefined(rhs)) {
             specialization_ = rhs;
             return;
         }
     }
+
+    if (jsop() == JSOP_STRICTEQ || jsop() == JSOP_STRICTNE) {
+        // 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;
+        }
+    }
 }
 
 MBitNot *
 MBitNot::New(MDefinition *input)
 {
     return new MBitNot(input);
 }
 
@@ -1257,16 +1274,41 @@ 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) {
+        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:
+          case MIRType_String:
+          case MIRType_Object:
+          case MIRType_Null:
+          case MIRType_Undefined:
+            *result = (op == JSOP_STRICTNE);
+            return true;
+          case MIRType_Boolean:
+            // Int32 specialization should handle this.
+            JS_NOT_REACHED("Wrong specialization");
+            return false;
+          default:
+            JS_NOT_REACHED("Unexpected type");
+            return false;
+        }
+    }
+
     return false;
 }
 
 MDefinition *
 MCompare::foldsTo(bool useValueNumbers)
 {
     bool result;
     if (tryFold(&result))
--- a/js/src/ion/MIR.h
+++ b/js/src/ion/MIR.h
@@ -1330,16 +1330,19 @@ class MCompare
 
     JSOp jsop() const {
         return jsop_;
     }
     TypePolicy *typePolicy() {
         return this;
     }
     AliasSet getAliasSet() const {
+        // Strict equality is never effectful.
+        if (jsop_ == JSOP_STRICTEQ || jsop_ == JSOP_STRICTNE)
+            return AliasSet::None();
         if (specialization_ == MIRType_None)
             return AliasSet::Store(AliasSet::Any);
         JS_ASSERT(specialization_ <= MIRType_Object);
         return AliasSet::None();
     }
 
   protected:
     bool congruentTo(MDefinition *const &ins) const {
@@ -4392,24 +4395,16 @@ class MInstanceOf
         setResultType(MIRType_Boolean);
     }
 
     INSTRUCTION_HEADER(InstanceOf);
 
     TypePolicy *typePolicy() {
         return this;
     }
-
-    MDefinition *lhs() const {
-        return getOperand(0);
-    }
-
-    MDefinition *rhs() const {
-        return getOperand(1);
-    }
 };
 
 // This is just a fake MIR Instruction wrapping a MConstant which has a
 // different MIRType than the Value type. This is used to prevent magic
 // unboxing.
 class MLazyArguments : public MConstant
 {
     MLazyArguments()
--- a/js/src/ion/TypePolicy.cpp
+++ b/js/src/ion/TypePolicy.cpp
@@ -142,16 +142,34 @@ ComparePolicy::adjustInputs(MInstruction
     if (specialization_ == MIRType_None)
         return BoxInputsPolicy::adjustInputs(def);
 
     if (IsNullOrUndefined(specialization_)) {
         // Nothing to do, lowering handles all types.
         return true;
     }
 
+    if (specialization_ == MIRType_Boolean) {
+        // The RHS is boolean, unbox if needed.
+        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(1)->type() == MIRType_Boolean);
+
+        // If the LHS is boolean, specialize as int32 instead.
+        if (def->getOperand(0)->type() == MIRType_Boolean)
+            specialization_ = MIRType_Int32;
+        return true;
+    }
+
     for (size_t i = 0; i < 2; i++) {
         MDefinition *in = def->getOperand(i);
         if (in->type() == specialization_)
             continue;
 
         MInstruction *replace;
 
         // See BinaryArithPolicy::adjustInputs for an explanation of the following
--- a/js/src/ion/arm/CodeGenerator-arm.cpp
+++ b/js/src/ion/arm/CodeGenerator-arm.cpp
@@ -1122,35 +1122,87 @@ CodeGeneratorARM::visitTestDAndBranch(LT
 }
 
 bool
 CodeGeneratorARM::visitCompareD(LCompareD *comp)
 {
     FloatRegister lhs = ToFloatRegister(comp->left());
     FloatRegister rhs = ToFloatRegister(comp->right());
 
-    Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->jsop());
+    Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop());
     masm.compareDouble(lhs, rhs);
     emitSet(Assembler::ConditionFromDoubleCondition(cond), ToRegister(comp->output()));
     return false;
 }
 
 bool
 CodeGeneratorARM::visitCompareDAndBranch(LCompareDAndBranch *comp)
 {
     FloatRegister lhs = ToFloatRegister(comp->left());
     FloatRegister rhs = ToFloatRegister(comp->right());
 
-    Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->jsop());
+    Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop());
     masm.compareDouble(lhs, rhs);
     emitBranch(Assembler::ConditionFromDoubleCondition(cond), comp->ifTrue(), comp->ifFalse());
     return true;
 }
 
 bool
+CodeGeneratorARM::visitCompareB(LCompareB *lir)
+{
+    MCompare *mir = lir->mir();
+
+    const ValueOperand lhs = ToValue(lir, LCompareB::Lhs);
+    const LAllocation *rhs = lir->rhs();
+    const Register output = ToRegister(lir->output());
+
+    JS_ASSERT(mir->jsop() == JSOP_STRICTEQ || mir->jsop() == JSOP_STRICTNE);
+
+    Label notBoolean, done;
+    masm.branchTestBoolean(Assembler::NotEqual, lhs, &notBoolean);
+    {
+        if (rhs->isConstant())
+            masm.cmp32(lhs.payloadReg(), Imm32(rhs->toConstant()->toBoolean()));
+        else
+            masm.cmp32(lhs.payloadReg(), ToRegister(rhs));
+        emitSet(JSOpToCondition(mir->jsop()), output);
+        masm.jump(&done);
+    }
+    masm.bind(&notBoolean);
+    {
+        masm.move32(Imm32(mir->jsop() == JSOP_STRICTNE), output);
+    }
+
+    masm.bind(&done);
+    return true;
+}
+
+bool
+CodeGeneratorARM::visitCompareBAndBranch(LCompareBAndBranch *lir)
+{
+    MCompare *mir = lir->mir();
+    const ValueOperand lhs = ToValue(lir, LCompareBAndBranch::Lhs);
+    const LAllocation *rhs = lir->rhs();
+
+    JS_ASSERT(mir->jsop() == JSOP_STRICTEQ || mir->jsop() == JSOP_STRICTNE);
+
+    if (mir->jsop() == JSOP_STRICTEQ)
+        masm.branchTestBoolean(Assembler::NotEqual, lhs, lir->ifFalse()->lir()->label());
+    else
+        masm.branchTestBoolean(Assembler::NotEqual, lhs, lir->ifTrue()->lir()->label());
+
+    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
 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
@@ -117,16 +117,18 @@ class CodeGeneratorARM : public CodeGene
     virtual bool visitShiftOp(LShiftOp *ins);
 
     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 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 visitTableSwitch(LTableSwitch *ins);
     virtual bool visitTruncateDToInt32(LTruncateDToInt32 *ins);
@@ -165,16 +167,17 @@ class CodeGeneratorARM : public CodeGene
     bool visitLoadElementT(LLoadElementT *load);
 
     bool visitGuardShape(LGuardShape *guard);
     bool visitGuardClass(LGuardClass *guard);
     bool visitImplicitThis(LImplicitThis *lir);
 
     bool visitRecompileCheck(LRecompileCheck *lir);
     bool visitInterruptCheck(LInterruptCheck *lir);
+
     bool generateInvalidateEpilogue();
 };
 
 typedef CodeGeneratorARM CodeGeneratorSpecific;
 
 // An out-of-line bailout thunk.
 class OutOfLineBailout : public OutOfLineCodeBase<CodeGeneratorARM>
 {
--- a/js/src/ion/shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/ion/shared/CodeGenerator-x86-shared.cpp
@@ -268,17 +268,17 @@ CodeGeneratorX86Shared::visitCompareAndB
 }
 
 bool
 CodeGeneratorX86Shared::visitCompareD(LCompareD *comp)
 {
     FloatRegister lhs = ToFloatRegister(comp->left());
     FloatRegister rhs = ToFloatRegister(comp->right());
 
-    Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->jsop());
+    Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop());
     masm.compareDouble(cond, lhs, rhs);
     emitSet(Assembler::ConditionFromDoubleCondition(cond), ToRegister(comp->output()),
             NaNCondFromDoubleCondition(cond));
     return true;
 }
 
 bool
 CodeGeneratorX86Shared::visitNotI(LNotI *ins)
@@ -300,17 +300,17 @@ CodeGeneratorX86Shared::visitNotD(LNotD 
 }
 
 bool
 CodeGeneratorX86Shared::visitCompareDAndBranch(LCompareDAndBranch *comp)
 {
     FloatRegister lhs = ToFloatRegister(comp->left());
     FloatRegister rhs = ToFloatRegister(comp->right());
 
-    Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->jsop());
+    Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop());
     masm.compareDouble(cond, lhs, rhs);
     emitBranch(Assembler::ConditionFromDoubleCondition(cond), comp->ifTrue(), comp->ifFalse(),
                NaNCondFromDoubleCondition(cond));
     return true;
 }
 
 bool
 CodeGeneratorX86Shared::generateOutOfLineCode()
--- a/js/src/ion/x64/CodeGenerator-x64.cpp
+++ b/js/src/ion/x64/CodeGenerator-x64.cpp
@@ -346,8 +346,52 @@ CodeGeneratorX64::visitInterruptCheck(LI
     void *interrupt = (void*)&gen->cx->runtime->interrupt;
     masm.movq(ImmWord(interrupt), ScratchReg);
     masm.cmpl(Operand(ScratchReg, 0), Imm32(0));
     masm.j(Assembler::NonZero, ool->entry());
     masm.bind(ool->rejoin());
     return true;
 }
 
+bool
+CodeGeneratorX64::visitCompareB(LCompareB *lir)
+{
+    MCompare *mir = lir->mir();
+
+    const ValueOperand lhs = ToValue(lir, LCompareB::Lhs);
+    const LAllocation *rhs = lir->rhs();
+    const Register output = ToRegister(lir->output());
+
+    JS_ASSERT(mir->jsop() == JSOP_STRICTEQ || mir->jsop() == JSOP_STRICTNE);
+
+    // Load boxed boolean in ScratchReg.
+    if (rhs->isConstant())
+        masm.moveValue(*rhs->toConstant(), ScratchReg);
+    else
+        masm.boxValue(JSVAL_TYPE_BOOLEAN, ToRegister(rhs), ScratchReg);
+
+    // Perform the comparison.
+    masm.cmpq(lhs.valueReg(), ScratchReg);
+    emitSet(JSOpToCondition(mir->jsop()), output);
+    return true;
+}
+
+bool
+CodeGeneratorX64::visitCompareBAndBranch(LCompareBAndBranch *lir)
+{
+    MCompare *mir = lir->mir();
+
+    const ValueOperand lhs = ToValue(lir, LCompareBAndBranch::Lhs);
+    const LAllocation *rhs = lir->rhs();
+
+    JS_ASSERT(mir->jsop() == JSOP_STRICTEQ || mir->jsop() == JSOP_STRICTNE);
+
+    // Load boxed boolean in ScratchReg.
+    if (rhs->isConstant())
+        masm.moveValue(*rhs->toConstant(), ScratchReg);
+    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;
+}
--- a/js/src/ion/x64/CodeGenerator-x64.h
+++ b/js/src/ion/x64/CodeGenerator-x64.h
@@ -77,16 +77,18 @@ class CodeGeneratorX64 : public CodeGene
     bool visitDouble(LDouble *ins);
     bool visitLoadSlotV(LLoadSlotV *ins);
     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);
 };
 
 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
@@ -346,8 +346,59 @@ CodeGeneratorX86::visitInterruptCheck(LI
 
     void *interrupt = (void*)&gen->cx->runtime->interrupt;
     masm.cmpl(Operand(interrupt), Imm32(0));
     masm.j(Assembler::NonZero, ool->entry());
     masm.bind(ool->rejoin());
     return true;
 }
 
+bool
+CodeGeneratorX86::visitCompareB(LCompareB *lir)
+{
+    MCompare *mir = lir->mir();
+
+    const ValueOperand lhs = ToValue(lir, LCompareB::Lhs);
+    const LAllocation *rhs = lir->rhs();
+    const Register output = ToRegister(lir->output());
+
+    JS_ASSERT(mir->jsop() == JSOP_STRICTEQ || mir->jsop() == JSOP_STRICTNE);
+
+    Label notBoolean, done;
+    masm.branchTestBoolean(Assembler::NotEqual, lhs, &notBoolean);
+    {
+        if (rhs->isConstant())
+            masm.cmp32(lhs.payloadReg(), Imm32(rhs->toConstant()->toBoolean()));
+        else
+            masm.cmp32(lhs.payloadReg(), ToRegister(rhs));
+        emitSet(JSOpToCondition(mir->jsop()), output);
+        masm.jump(&done);
+    }
+    masm.bind(&notBoolean);
+    {
+        masm.move32(Imm32(mir->jsop() == JSOP_STRICTNE), output);
+    }
+
+    masm.bind(&done);
+    return true;
+}
+
+bool
+CodeGeneratorX86::visitCompareBAndBranch(LCompareBAndBranch *lir)
+{
+    MCompare *mir = lir->mir();
+    const ValueOperand lhs = ToValue(lir, LCompareBAndBranch::Lhs);
+    const LAllocation *rhs = lir->rhs();
+
+    JS_ASSERT(mir->jsop() == JSOP_STRICTEQ || mir->jsop() == JSOP_STRICTNE);
+
+    if (mir->jsop() == JSOP_STRICTEQ)
+        masm.branchTestBoolean(Assembler::NotEqual, lhs, lir->ifFalse()->lir()->label());
+    else
+        masm.branchTestBoolean(Assembler::NotEqual, lhs, lir->ifTrue()->lir()->label());
+
+    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;
+}
--- a/js/src/ion/x86/CodeGenerator-x86.h
+++ b/js/src/ion/x86/CodeGenerator-x86.h
@@ -96,16 +96,18 @@ class CodeGeneratorX86 : public CodeGene
     bool visitDouble(LDouble *ins);
     bool visitLoadSlotV(LLoadSlotV *load);
     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);
 };
 
 typedef CodeGeneratorX86 CodeGeneratorSpecific;
 
 } // namespace ion
 } // namespace js
 
 #endif // jsion_codegen_x86_h__
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug766218.js
@@ -0,0 +1,66 @@
+// Test strict-equality with a constant boolean.
+function test1() {
+    var a = [{}, false, true, 0];
+    var res = 0;
+
+    for (var i=0; i<100; i++) {
+        if (a[i % 4] === false)
+            res += 1;
+    }
+    assertEq(res, 25);
+
+    res = 0;
+    for (var i=0; i<100; i++) {
+        if (true !== a[i % 4])
+            res += 1;
+    }
+    assertEq(res, 75);
+
+    res = 0;
+    for (var i=0; i<100; i++) {
+        res += (a[i % 4] === true);
+    }
+    assertEq(res, 25);
+
+    res = 0;
+    for (var i=0; i<100; i++) {
+        res += (false !== a[i % 4]);
+    }
+    assertEq(res, 75);
+}
+test1();
+
+// Test strict-equality with non-constant boolean.
+var TRUE = true;
+var FALSE = false;
+
+function test2() {
+    var a = [{}, false, true, 0];
+    var res = 0;
+
+    for (var i=0; i<100; i++) {
+        if (a[i % 4] === FALSE)
+            res += 1;
+    }
+    assertEq(res, 25);
+
+    res = 0;
+    for (var i=0; i<100; i++) {
+        if (TRUE !== a[i % 4])
+            res += 1;
+    }
+    assertEq(res, 75);
+
+    res = 0;
+    for (var i=0; i<100; i++) {
+        res += (a[i % 4] === TRUE);
+    }
+    assertEq(res, 25);
+
+    res = 0;
+    for (var i=0; i<100; i++) {
+        res += (FALSE !== a[i % 4]);
+    }
+    assertEq(res, 75);
+}
+test2();