Bug 913282: More Float32 operators: comparisons; p=dougc,bbouvier, r=jonco,h4writer
authorBenjamin Bouvier <benj@benj.me>
Tue, 22 Oct 2013 14:51:37 +0200
changeset 165492 03fdc9e5eea382176c6be74b6fbef9cc6822e16e
parent 165491 77b72ea510d0b8bb05f1d039db6d85c9d103d225
child 165493 b00081178fe7250fddfa8e886b9b81c88d0a16cd
push id3066
push userakeybl@mozilla.com
push dateMon, 09 Dec 2013 19:58:46 +0000
treeherdermozilla-beta@a31a0dce83aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjonco, h4writer
bugs913282
milestone27.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 913282: More Float32 operators: comparisons; p=dougc,bbouvier, r=jonco,h4writer dougc for the ARM parts, bbouvier for the rest
js/src/jit/LIR-Common.h
js/src/jit/LOpcodes.h
js/src/jit/Lowering.cpp
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jit/TypePolicy.cpp
js/src/jit/arm/CodeGenerator-arm.cpp
js/src/jit/arm/CodeGenerator-arm.h
js/src/jit/arm/MacroAssembler-arm.cpp
js/src/jit/arm/MacroAssembler-arm.h
js/src/jit/shared/CodeGenerator-x86-shared.cpp
js/src/jit/shared/CodeGenerator-x86-shared.h
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -1394,16 +1394,37 @@ class LTestDAndBranch : public LControlI
     MBasicBlock *ifTrue() const {
         return getSuccessor(0);
     }
     MBasicBlock *ifFalse() const {
         return getSuccessor(1);
     }
 };
 
+// Takes in either an integer or boolean input and tests it for truthiness.
+class LTestFAndBranch : public LControlInstructionHelper<2, 1, 0>
+{
+  public:
+    LIR_HEADER(TestFAndBranch)
+
+    LTestFAndBranch(const LAllocation &in, MBasicBlock *ifTrue, MBasicBlock *ifFalse)
+    {
+        setOperand(0, in);
+        setSuccessor(0, ifTrue);
+        setSuccessor(1, ifFalse);
+    }
+
+    MBasicBlock *ifTrue() const {
+        return getSuccessor(0);
+    }
+    MBasicBlock *ifFalse() const {
+        return getSuccessor(1);
+    }
+};
+
 // Takes an object and tests it for truthiness.  An object is falsy iff it
 // emulates |undefined|; see js::EmulatesUndefined.
 class LTestOAndBranch : public LControlInstructionHelper<2, 1, 1>
 {
   public:
     LIR_HEADER(TestOAndBranch)
 
     LTestOAndBranch(const LAllocation &input, MBasicBlock *ifTruthy, MBasicBlock *ifFalsy,
@@ -1606,16 +1627,36 @@ class LCompareD : public LInstructionHel
     const LAllocation *right() {
         return getOperand(1);
     }
     MCompare *mir() {
         return mir_->toCompare();
     }
 };
 
+class LCompareF : public LInstructionHelper<1, 2, 0>
+{
+  public:
+    LIR_HEADER(CompareF)
+    LCompareF(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 LControlInstructionHelper<2, 2, 0>
 {
   public:
     LIR_HEADER(CompareDAndBranch)
     LCompareDAndBranch(const LAllocation &left, const LAllocation &right,
                        MBasicBlock *ifTrue, MBasicBlock *ifFalse)
     {
         setOperand(0, left);
@@ -1636,16 +1677,46 @@ class LCompareDAndBranch : public LContr
     const LAllocation *right() {
         return getOperand(1);
     }
     MCompare *mir() {
         return mir_->toCompare();
     }
 };
 
+class LCompareFAndBranch : public LControlInstructionHelper<2, 2, 0>
+{
+  public:
+    LIR_HEADER(CompareFAndBranch)
+    LCompareFAndBranch(const LAllocation &left, const LAllocation &right,
+                       MBasicBlock *ifTrue, MBasicBlock *ifFalse)
+    {
+        setOperand(0, left);
+        setOperand(1, right);
+        setSuccessor(0, ifTrue);
+        setSuccessor(1, ifFalse);
+    }
+
+    MBasicBlock *ifTrue() const {
+        return getSuccessor(0);
+    }
+    MBasicBlock *ifFalse() const {
+        return getSuccessor(1);
+    }
+    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);
--- a/js/src/jit/LOpcodes.h
+++ b/js/src/jit/LOpcodes.h
@@ -66,24 +66,27 @@
     _(BitOpV)                       \
     _(ShiftI)                       \
     _(UrshD)                        \
     _(Return)                       \
     _(Throw)                        \
     _(Phi)                          \
     _(TestIAndBranch)               \
     _(TestDAndBranch)               \
+    _(TestFAndBranch)               \
     _(TestVAndBranch)               \
     _(TestOAndBranch)               \
     _(FunctionDispatch)             \
     _(TypeObjectDispatch)           \
     _(Compare)                      \
     _(CompareAndBranch)             \
     _(CompareD)                     \
     _(CompareDAndBranch)            \
+    _(CompareF)                     \
+    _(CompareFAndBranch)            \
     _(CompareS)                     \
     _(CompareStrictS)               \
     _(CompareB)                     \
     _(CompareBAndBranch)            \
     _(CompareV)                     \
     _(CompareVAndBranch)            \
     _(CompareVM)                    \
     _(BitAndAndBranch)              \
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -656,16 +656,22 @@ LIRGenerator::visitTest(MTest *test)
         return add(new LGoto(ifFalse));
 
     // Constant Double operand.
     if (opd->type() == MIRType_Double && opd->isConstant()) {
         bool result = ToBoolean(opd->toConstant()->value());
         return add(new LGoto(result ? ifTrue : ifFalse));
     }
 
+    // Constant Float32 operand.
+    if (opd->type() == MIRType_Float32 && opd->isConstant()) {
+        bool result = ToBoolean(opd->toConstant()->value());
+        return add(new LGoto(result ? ifTrue : ifFalse));
+    }
+
     // Constant Int32 operand.
     if (opd->type() == MIRType_Int32 && opd->isConstant()) {
         int32_t num = opd->toConstant()->value().toInt32();
         return add(new LGoto(num ? ifTrue : ifFalse));
     }
 
     // Constant Boolean operand.
     if (opd->type() == MIRType_Boolean && opd->isConstant()) {
@@ -753,16 +759,24 @@ LIRGenerator::visitTest(MTest *test)
         // Compare and branch doubles.
         if (comp->isDoubleComparison()) {
             LAllocation lhs = useRegister(left);
             LAllocation rhs = useRegister(right);
             LCompareDAndBranch *lir = new LCompareDAndBranch(lhs, rhs, ifTrue, ifFalse);
             return add(lir, comp);
         }
 
+        // Compare and branch floats.
+        if (comp->isFloat32Comparison()) {
+            LAllocation lhs = useRegister(left);
+            LAllocation rhs = useRegister(right);
+            LCompareFAndBranch *lir = new LCompareFAndBranch(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);
@@ -778,16 +792,19 @@ LIRGenerator::visitTest(MTest *test)
             ReorderCommutative(&lhs, &rhs);
             return lowerForBitAndAndBranch(new LBitAndAndBranch(ifTrue, ifFalse), test, lhs, rhs);
         }
     }
 
     if (opd->type() == MIRType_Double)
         return add(new LTestDAndBranch(useRegister(opd), ifTrue, ifFalse));
 
+    if (opd->type() == MIRType_Float32)
+        return add(new LTestFAndBranch(useRegister(opd), ifTrue, ifFalse));
+
     JS_ASSERT(opd->type() == MIRType_Int32 || opd->type() == MIRType_Boolean);
     return add(new LTestIAndBranch(useRegister(opd), ifTrue, ifFalse));
 }
 
 bool
 LIRGenerator::visitFunctionDispatch(MFunctionDispatch *ins)
 {
     LFunctionDispatch *lir = new LFunctionDispatch(useRegister(ins->input()));
@@ -926,16 +943,20 @@ LIRGenerator::visitCompare(MCompare *com
         }
         return define(new LCompare(op, lhs, rhs), comp);
     }
 
     // Compare doubles.
     if (comp->isDoubleComparison())
         return define(new LCompareD(useRegister(left), useRegister(right)), comp);
 
+    // Compare float32.
+    if (comp->isFloat32Comparison())
+        return define(new LCompareF(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);
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -661,17 +661,17 @@ MCompare::New(MDefinition *left, MDefini
 {
     return new MCompare(left, right, op);
 }
 
 MCompare *
 MCompare::NewAsmJS(MDefinition *left, MDefinition *right, JSOp op, CompareType compareType)
 {
     JS_ASSERT(compareType == Compare_Int32 || compareType == Compare_UInt32 ||
-              compareType == Compare_Double);
+              compareType == Compare_Double || compareType == Compare_Float32);
     MCompare *comp = new MCompare(left, right, op);
     comp->compareType_ = compareType;
     comp->operandMightEmulateUndefined_ = false;
     comp->setResultType(MIRType_Int32);
     return comp;
 }
 
 MTableSwitch *
@@ -1675,16 +1675,18 @@ MCompare::inputType()
         return MIRType_Boolean;
       case Compare_UInt32:
       case Compare_Int32:
         return MIRType_Int32;
       case Compare_Double:
       case Compare_DoubleMaybeCoerceLHS:
       case Compare_DoubleMaybeCoerceRHS:
         return MIRType_Double;
+      case Compare_Float32:
+        return MIRType_Float32;
       case Compare_String:
       case Compare_StrictString:
         return MIRType_String;
       case Compare_Object:
         return MIRType_Object;
       case Compare_Unknown:
       case Compare_Value:
         return MIRType_Value;
@@ -2201,16 +2203,17 @@ MCompare::tryFold(bool *result)
             }
             return true;
           case MIRType_Object:
             if ((op == JSOP_EQ || op == JSOP_NE) && operandMightEmulateUndefined())
                 return false;
             /* FALL THROUGH */
           case MIRType_Int32:
           case MIRType_Double:
+          case MIRType_Float32:
           case MIRType_String:
           case MIRType_Boolean:
             *result = (op == JSOP_NE || op == JSOP_STRICTNE);
             return true;
           default:
             MOZ_ASSUME_UNREACHABLE("Unexpected type");
         }
     }
@@ -2219,16 +2222,17 @@ MCompare::tryFold(bool *result)
         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_Float32:
           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.
@@ -2243,16 +2247,17 @@ MCompare::tryFold(bool *result)
         JS_ASSERT(rhs()->type() == MIRType_String);
 
         switch (lhs()->type()) {
           case MIRType_Value:
             return false;
           case MIRType_Boolean:
           case MIRType_Int32:
           case MIRType_Double:
+          case MIRType_Float32:
           case MIRType_Object:
           case MIRType_Null:
           case MIRType_Undefined:
             *result = (op == JSOP_STRICTNE);
             return true;
           case MIRType_String:
             // Compare_String specialization should handle this.
             MOZ_ASSUME_UNREACHABLE("Wrong specialization");
@@ -2388,16 +2393,35 @@ MCompare::foldsTo(bool useValueNumbers)
         JS_ASSERT(type() == MIRType_Boolean);
         return MConstant::New(BooleanValue(result));
     }
 
     return this;
 }
 
 void
+MCompare::trySpecializeFloat32()
+{
+    MDefinition *lhs = getOperand(0);
+    MDefinition *rhs = getOperand(1);
+
+    if (compareType_ == Compare_Float32)
+        return;
+
+    if (lhs->canProduceFloat32() && rhs->canProduceFloat32() && compareType_ == Compare_Double) {
+        compareType_ = Compare_Float32;
+    } else {
+        if (lhs->type() == MIRType_Float32)
+            ConvertDefinitionToDouble<0>(lhs, this);
+        if (rhs->type() == MIRType_Float32)
+            ConvertDefinitionToDouble<1>(rhs, this);
+    }
+}
+
+void
 MNot::infer()
 {
     JS_ASSERT(operandMightEmulateUndefined());
 
     if (!MaybeEmulatesUndefined(getOperand(0)))
         markOperandCantEmulateUndefined();
 }
 
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -2106,16 +2106,19 @@ class MCompare
         Compare_UInt32,
 
         // Double compared to Double
         Compare_Double,
 
         Compare_DoubleMaybeCoerceLHS,
         Compare_DoubleMaybeCoerceRHS,
 
+        // Float compared to Float
+        Compare_Float32,
+
         // String compared to String
         Compare_String,
 
         // Undefined compared to String
         // Null      compared to String
         // Boolean   compared to String
         // Int32     compared to String
         // Double    compared to String
@@ -2161,16 +2164,19 @@ class MCompare
     CompareType compareType() const {
         return compareType_;
     }
     bool isDoubleComparison() const {
         return compareType() == Compare_Double ||
                compareType() == Compare_DoubleMaybeCoerceLHS ||
                compareType() == Compare_DoubleMaybeCoerceRHS;
     }
+    bool isFloat32Comparison() const {
+        return compareType() == Compare_Float32;
+    }
     void setCompareType(CompareType type) {
         compareType_ = type;
     }
     MIRType inputType();
 
     JSOp jsop() const {
         return jsop_;
     }
@@ -2190,16 +2196,25 @@ class MCompare
         if (compareType_ == Compare_Unknown)
             return AliasSet::Store(AliasSet::Any);
         JS_ASSERT(compareType_ <= Compare_Value);
         return AliasSet::None();
     }
 
     void printOpcode(FILE *fp) const;
 
+    void trySpecializeFloat32();
+    bool isFloat32Commutative() const { return true; }
+
+# ifdef DEBUG
+    bool isConsistentFloat32Use() const {
+        return compareType_ == Compare_Float32;
+    }
+# endif
+
   protected:
     bool congruentTo(MDefinition *ins) const {
         if (!MBinaryInstruction::congruentTo(ins))
             return false;
         return compareType() == ins->toCompare()->compareType() &&
                jsop() == ins->toCompare()->jsop();
     }
 };
--- a/js/src/jit/TypePolicy.cpp
+++ b/js/src/jit/TypePolicy.cpp
@@ -193,17 +193,17 @@ ComparePolicy::adjustInputs(MInstruction
     {
         // Nothing to do for undefined and null, lowering handles all types.
         return true;
     }
 
     // Convert all inputs to the right input type
     MIRType type = compare->inputType();
     JS_ASSERT(type == MIRType_Int32 || type == MIRType_Double ||
-              type == MIRType_Object || type == MIRType_String);
+              type == MIRType_Object || type == MIRType_String || type == MIRType_Float32);
     for (size_t i = 0; i < 2; i++) {
         MDefinition *in = def->getOperand(i);
         if (in->type() == type)
             continue;
 
         MInstruction *replace;
 
         // See BinaryArithPolicy::adjustInputs for an explanation of the following
@@ -223,16 +223,30 @@ ComparePolicy::adjustInputs(MInstruction
             if (in->type() == MIRType_Null ||
                 (in->type() == MIRType_Boolean && convert == MToDouble::NumbersOnly))
             {
                 in = boxAt(def, in);
             }
             replace = MToDouble::New(in, convert);
             break;
           }
+          case MIRType_Float32: {
+            MToFloat32::ConversionKind convert = MToFloat32::NumbersOnly;
+            if (compare->compareType() == MCompare::Compare_DoubleMaybeCoerceLHS && i == 0)
+                convert = MToFloat32::NonNullNonStringPrimitives;
+            else if (compare->compareType() == MCompare::Compare_DoubleMaybeCoerceRHS && i == 1)
+                convert = MToFloat32::NonNullNonStringPrimitives;
+            if (in->type() == MIRType_Null ||
+                (in->type() == MIRType_Boolean && convert == MToFloat32::NumbersOnly))
+            {
+                in = boxAt(def, in);
+            }
+            replace = MToFloat32::New(in, convert);
+            break;
+          }
           case MIRType_Int32:
             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);
@@ -298,16 +312,17 @@ TestPolicy::adjustInputs(MInstruction *i
     MDefinition *op = ins->getOperand(0);
     switch (op->type()) {
       case MIRType_Value:
       case MIRType_Null:
       case MIRType_Undefined:
       case MIRType_Boolean:
       case MIRType_Int32:
       case MIRType_Double:
+      case MIRType_Float32:
       case MIRType_Object:
         break;
 
       case MIRType_String:
       {
         MStringLength *length = MStringLength::New(op);
         ins->block()->insertBefore(ins, length);
         ins->replaceOperand(0, length);
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -1380,40 +1380,83 @@ CodeGeneratorARM::visitTestDAndBranch(LT
     // it is also false if one of the operands is NAN, which is
     // shown as Overflow.
     jumpToBlock(ifFalse, Assembler::Overflow);
     jumpToBlock(ifTrue);
     return true;
 }
 
 bool
+CodeGeneratorARM::visitTestFAndBranch(LTestFAndBranch *test)
+{
+    const LAllocation *opd = test->input();
+    masm.ma_vcmpz_f32(ToFloatRegister(opd));
+    masm.as_vmrs(pc);
+
+    MBasicBlock *ifTrue = test->ifTrue();
+    MBasicBlock *ifFalse = test->ifFalse();
+    // If the compare set the  0 bit, then the result
+    // is definately false.
+    jumpToBlock(ifFalse, Assembler::Zero);
+    // it is also false if one of the operands is NAN, which is
+    // shown as Overflow.
+    jumpToBlock(ifFalse, Assembler::Overflow);
+    jumpToBlock(ifTrue);
+    return true;
+}
+
+bool
 CodeGeneratorARM::visitCompareD(LCompareD *comp)
 {
     FloatRegister lhs = ToFloatRegister(comp->left());
     FloatRegister rhs = ToFloatRegister(comp->right());
 
     Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop());
     masm.compareDouble(lhs, rhs);
     masm.emitSet(Assembler::ConditionFromDoubleCondition(cond), ToRegister(comp->output()));
     return true;
 }
 
 bool
+CodeGeneratorARM::visitCompareF(LCompareF *comp)
+{
+    FloatRegister lhs = ToFloatRegister(comp->left());
+    FloatRegister rhs = ToFloatRegister(comp->right());
+
+    Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop());
+    masm.compareFloat(lhs, rhs);
+    masm.emitSet(Assembler::ConditionFromDoubleCondition(cond), ToRegister(comp->output()));
+    return true;
+}
+
+bool
 CodeGeneratorARM::visitCompareDAndBranch(LCompareDAndBranch *comp)
 {
     FloatRegister lhs = ToFloatRegister(comp->left());
     FloatRegister rhs = ToFloatRegister(comp->right());
 
     Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop());
     masm.compareDouble(lhs, rhs);
     emitBranch(Assembler::ConditionFromDoubleCondition(cond), comp->ifTrue(), comp->ifFalse());
     return true;
 }
 
 bool
+CodeGeneratorARM::visitCompareFAndBranch(LCompareFAndBranch *comp)
+{
+    FloatRegister lhs = ToFloatRegister(comp->left());
+    FloatRegister rhs = ToFloatRegister(comp->right());
+
+    Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop());
+    masm.compareFloat(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());
 
--- a/js/src/jit/arm/CodeGenerator-arm.h
+++ b/js/src/jit/arm/CodeGenerator-arm.h
@@ -84,18 +84,21 @@ class CodeGeneratorARM : public CodeGene
     virtual bool visitPowHalfD(LPowHalfD *ins);
     virtual bool visitShiftI(LShiftI *ins);
     virtual bool visitUrshD(LUrshD *ins);
 
     virtual bool visitTestIAndBranch(LTestIAndBranch *test);
     virtual bool visitCompare(LCompare *comp);
     virtual bool visitCompareAndBranch(LCompareAndBranch *comp);
     virtual bool visitTestDAndBranch(LTestDAndBranch *test);
+    virtual bool visitTestFAndBranch(LTestFAndBranch *test);
     virtual bool visitCompareD(LCompareD *comp);
+    virtual bool visitCompareF(LCompareF *comp);
     virtual bool visitCompareDAndBranch(LCompareDAndBranch *comp);
+    virtual bool visitCompareFAndBranch(LCompareFAndBranch *comp);
     virtual bool visitCompareB(LCompareB *lir);
     virtual bool visitCompareBAndBranch(LCompareBAndBranch *lir);
     virtual bool visitCompareV(LCompareV *lir);
     virtual bool visitCompareVAndBranch(LCompareVAndBranch *lir);
     virtual bool visitBitAndAndBranch(LBitAndAndBranch *baab);
     virtual bool visitUInt32ToDouble(LUInt32ToDouble *lir);
     virtual bool visitNotI(LNotI *ins);
     virtual bool visitNotD(LNotD *ins);
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -1520,16 +1520,21 @@ MacroAssemblerARM::ma_vcmp_f32(FloatRegi
 {
     as_vcmp(VFPRegister(src1).singleOverlay(), VFPRegister(src2).singleOverlay(), cc);
 }
 void
 MacroAssemblerARM::ma_vcmpz(FloatRegister src1, Condition cc)
 {
     as_vcmpz(VFPRegister(src1), cc);
 }
+void
+MacroAssemblerARM::ma_vcmpz_f32(FloatRegister src1, Condition cc)
+{
+    as_vcmpz(VFPRegister(src1).singleOverlay(), cc);
+}
 
 void
 MacroAssemblerARM::ma_vcvt_F64_I32(FloatRegister src, FloatRegister dest, Condition cc)
 {
     as_vcvt(VFPRegister(dest).sintOverlay(), VFPRegister(src), false, cc);
 }
 void
 MacroAssemblerARM::ma_vcvt_F64_U32(FloatRegister src, FloatRegister dest, Condition cc)
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -321,16 +321,17 @@ class MacroAssemblerARM : public Assembl
     void ma_vsqrt(FloatRegister src, FloatRegister dest, Condition cc = Always);
 
     void ma_vimm(double value, FloatRegister dest, Condition cc = Always);
     void ma_vimm_f32(float value, FloatRegister dest, Condition cc = Always);
 
     void ma_vcmp(FloatRegister src1, FloatRegister src2, Condition cc = Always);
     void ma_vcmp_f32(FloatRegister src1, FloatRegister src2, Condition cc = Always);
     void ma_vcmpz(FloatRegister src1, Condition cc = Always);
+    void ma_vcmpz_f32(FloatRegister src1, Condition cc = Always);
 
     void ma_vadd_f32(FloatRegister src1, FloatRegister src2, FloatRegister dst);
     void ma_vsub_f32(FloatRegister src1, FloatRegister src2, FloatRegister dst);
 
     void ma_vmul_f32(FloatRegister src1, FloatRegister src2, FloatRegister dst);
     void ma_vdiv_f32(FloatRegister src1, FloatRegister src2, FloatRegister dst);
 
     void ma_vneg_f32(FloatRegister src, FloatRegister dest, Condition cc = Always);
--- a/js/src/jit/shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-x86-shared.cpp
@@ -124,16 +124,27 @@ CodeGeneratorX86Shared::visitTestDAndBra
     // enough to determine which branch to take.
     masm.xorpd(ScratchFloatReg, ScratchFloatReg);
     masm.ucomisd(ToFloatRegister(opd), ScratchFloatReg);
     emitBranch(Assembler::NotEqual, test->ifTrue(), test->ifFalse());
     return true;
 }
 
 bool
+CodeGeneratorX86Shared::visitTestFAndBranch(LTestFAndBranch *test)
+{
+    const LAllocation *opd = test->input();
+    // ucomiss flags are the same as doubles; see comment above
+    masm.xorps(ScratchFloatReg, ScratchFloatReg);
+    masm.ucomiss(ToFloatRegister(opd), ScratchFloatReg);
+    emitBranch(Assembler::NotEqual, test->ifTrue(), test->ifFalse());
+    return true;
+}
+
+bool
 CodeGeneratorX86Shared::visitBitAndAndBranch(LBitAndAndBranch *baab)
 {
     if (baab->right()->isConstant())
         masm.testl(ToRegister(baab->left()), Imm32(ToInt32(baab->right())));
     else
         masm.testl(ToRegister(baab->left()), ToRegister(baab->right()));
     emitBranch(Assembler::NonZero, baab->ifTrue(), baab->ifFalse());
     return true;
@@ -183,16 +194,29 @@ CodeGeneratorX86Shared::visitCompareD(LC
     Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop());
     masm.compareDouble(cond, lhs, rhs);
     masm.emitSet(Assembler::ConditionFromDoubleCondition(cond), ToRegister(comp->output()),
             Assembler::NaNCondFromDoubleCondition(cond));
     return true;
 }
 
 bool
+CodeGeneratorX86Shared::visitCompareF(LCompareF *comp)
+{
+    FloatRegister lhs = ToFloatRegister(comp->left());
+    FloatRegister rhs = ToFloatRegister(comp->right());
+
+    Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop());
+    masm.compareFloat(cond, lhs, rhs);
+    masm.emitSet(Assembler::ConditionFromDoubleCondition(cond), ToRegister(comp->output()),
+            Assembler::NaNCondFromDoubleCondition(cond));
+    return true;
+}
+
+bool
 CodeGeneratorX86Shared::visitNotI(LNotI *ins)
 {
     masm.cmpl(ToRegister(ins->input()), Imm32(0));
     masm.emitSet(Assembler::Equal, ToRegister(ins->output()));
     return true;
 }
 
 bool
@@ -215,16 +239,29 @@ CodeGeneratorX86Shared::visitCompareDAnd
     Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop());
     masm.compareDouble(cond, lhs, rhs);
     emitBranch(Assembler::ConditionFromDoubleCondition(cond), comp->ifTrue(), comp->ifFalse(),
                Assembler::NaNCondFromDoubleCondition(cond));
     return true;
 }
 
 bool
+CodeGeneratorX86Shared::visitCompareFAndBranch(LCompareFAndBranch *comp)
+{
+    FloatRegister lhs = ToFloatRegister(comp->left());
+    FloatRegister rhs = ToFloatRegister(comp->right());
+
+    Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop());
+    masm.compareFloat(cond, lhs, rhs);
+    emitBranch(Assembler::ConditionFromDoubleCondition(cond), comp->ifTrue(), comp->ifFalse(),
+               Assembler::NaNCondFromDoubleCondition(cond));
+    return true;
+}
+
+bool
 CodeGeneratorX86Shared::visitAsmJSPassStackArg(LAsmJSPassStackArg *ins)
 {
     const MAsmJSPassStackArg *mir = ins->mir();
     Address dst(StackPointer, mir->spOffset());
     if (ins->arg()->isConstant()) {
         masm.storePtr(ImmWord(ToInt32(ins->arg())), dst);
     } else {
         if (ins->arg()->isGeneralReg())
--- a/js/src/jit/shared/CodeGenerator-x86-shared.h
+++ b/js/src/jit/shared/CodeGenerator-x86-shared.h
@@ -93,20 +93,23 @@ class CodeGeneratorX86Shared : public Co
     virtual bool visitModI(LModI *ins);
     virtual bool visitModPowTwoI(LModPowTwoI *ins);
     virtual bool visitBitNotI(LBitNotI *ins);
     virtual bool visitBitOpI(LBitOpI *ins);
     virtual bool visitShiftI(LShiftI *ins);
     virtual bool visitUrshD(LUrshD *ins);
     virtual bool visitTestIAndBranch(LTestIAndBranch *test);
     virtual bool visitTestDAndBranch(LTestDAndBranch *test);
+    virtual bool visitTestFAndBranch(LTestFAndBranch *test);
     virtual bool visitCompare(LCompare *comp);
     virtual bool visitCompareAndBranch(LCompareAndBranch *comp);
     virtual bool visitCompareD(LCompareD *comp);
     virtual bool visitCompareDAndBranch(LCompareDAndBranch *comp);
+    virtual bool visitCompareF(LCompareF *comp);
+    virtual bool visitCompareFAndBranch(LCompareFAndBranch *comp);
     virtual bool visitBitAndAndBranch(LBitAndAndBranch *baab);
     virtual bool visitNotI(LNotI *comp);
     virtual bool visitNotD(LNotD *comp);
     virtual bool visitMathD(LMathD *math);
     virtual bool visitMathF(LMathF *math);
     virtual bool visitFloor(LFloor *lir);
     virtual bool visitRound(LRound *lir);
     virtual bool visitGuardShape(LGuardShape *guard);