Bug 919838: Specialize ToInt32 for Float32; r=sstangl
authorBenjamin Bouvier <bbouvier@mozilla.com>
Thu, 26 Sep 2013 18:11:31 -0700
changeset 162733 3c21fdf48606c2996416d020bbd21fd2eb069dfb
parent 162732 4e1a3919e741eb8e3a4f520535671df9711b5b9b
child 162734 05af5499a03797ac2dfad1cc5200d99db3fc926f
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)
reviewerssstangl
bugs919838
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 919838: Specialize ToInt32 for Float32; r=sstangl
js/src/assembler/assembler/X86Assembler.h
js/src/jit/CodeGenerator.cpp
js/src/jit/CodeGenerator.h
js/src/jit/LIR-Common.h
js/src/jit/LOpcodes.h
js/src/jit/Lowering.cpp
js/src/jit/MIR.h
js/src/jit/arm/MacroAssembler-arm.cpp
js/src/jit/arm/MacroAssembler-arm.h
js/src/jit/shared/Assembler-x86-shared.h
js/src/jit/shared/MacroAssembler-x86-shared.h
--- a/js/src/assembler/assembler/X86Assembler.h
+++ b/js/src/assembler/assembler/X86Assembler.h
@@ -2454,16 +2454,23 @@ public:
     void movmskpd_rr(XMMRegisterID src, RegisterID dst)
     {
         spew("movmskpd   %s, %s",
              nameFPReg(src), nameIReg(dst));
         m_formatter.prefix(PRE_SSE_66);
         m_formatter.twoByteOp(OP2_MOVMSKPD_EdVd, dst, (RegisterID)src);
     }
 
+    void movmskps_rr(XMMRegisterID src, RegisterID dst)
+    {
+        spew("movmskps   %s, %s",
+             nameFPReg(src), nameIReg(dst));
+        m_formatter.twoByteOp(OP2_MOVMSKPD_EdVd, dst, (RegisterID)src);
+    }
+
     void ptest_rr(XMMRegisterID lhs, XMMRegisterID rhs) {
         spew("ptest      %s, %s",
              nameFPReg(lhs), nameFPReg(rhs));
         m_formatter.prefix(PRE_SSE_66);
         m_formatter.threeByteOp(OP3_PTEST_VdVd, ESCAPE_PTEST, (RegisterID)rhs, (RegisterID)lhs);
     }
 
     void movd_rr(XMMRegisterID src, RegisterID dst)
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -356,16 +356,28 @@ CodeGenerator::visitDoubleToInt32(LDoubl
     FloatRegister input = ToFloatRegister(lir->input());
     Register output = ToRegister(lir->output());
     masm.convertDoubleToInt32(input, output, &fail, lir->mir()->canBeNegativeZero());
     if (!bailoutFrom(&fail, lir->snapshot()))
         return false;
     return true;
 }
 
+bool
+CodeGenerator::visitFloat32ToInt32(LFloat32ToInt32 *lir)
+{
+    Label fail;
+    FloatRegister input = ToFloatRegister(lir->input());
+    Register output = ToRegister(lir->output());
+    masm.convertFloat32ToInt32(input, output, &fail, lir->mir()->canBeNegativeZero());
+    if (!bailoutFrom(&fail, lir->snapshot()))
+        return false;
+    return true;
+}
+
 void
 CodeGenerator::emitOOLTestObject(Register objreg, Label *ifTruthy, Label *ifFalsy, Register scratch)
 {
     saveVolatile(scratch);
     masm.setupUnalignedABICall(1, scratch);
     masm.passABIArg(objreg);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, js::EmulatesUndefined));
     masm.storeCallResult(scratch);
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -115,16 +115,17 @@ class CodeGenerator : public CodeGenerat
     void emitPushArguments(LApplyArgsGeneric *apply, Register extraStackSpace);
     void emitPopArguments(LApplyArgsGeneric *apply, Register extraStackSize);
     bool visitApplyArgsGeneric(LApplyArgsGeneric *apply);
     bool visitBail(LBail *lir);
     bool visitGetDynamicName(LGetDynamicName *lir);
     bool visitFilterArguments(LFilterArguments *lir);
     bool visitCallDirectEval(LCallDirectEval *lir);
     bool visitDoubleToInt32(LDoubleToInt32 *lir);
+    bool visitFloat32ToInt32(LFloat32ToInt32 *lir);
     bool visitNewSlots(LNewSlots *lir);
     bool visitNewParallelArrayVMCall(LNewParallelArray *lir);
     bool visitNewParallelArray(LNewParallelArray *lir);
     bool visitOutOfLineNewParallelArray(OutOfLineNewParallelArray *ool);
     bool visitNewArrayCallVM(LNewArray *lir);
     bool visitNewArray(LNewArray *lir);
     bool visitOutOfLineNewArray(OutOfLineNewArray *ool);
     bool visitNewObjectVMCall(LNewObject *lir);
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -2667,16 +2667,33 @@ class LDoubleToInt32 : public LInstructi
         setOperand(0, in);
     }
 
     MToInt32 *mir() const {
         return mir_->toToInt32();
     }
 };
 
+// Convert a float32 to an int32.
+//   Input: floating-point register
+//   Output: 32-bit integer
+//   Bailout: if the float32 cannot be converted to an integer.
+class LFloat32ToInt32 : public LInstructionHelper<1, 1, 0>
+{
+  public:
+    LIR_HEADER(Float32ToInt32)
+
+    LFloat32ToInt32(const LAllocation &in) {
+        setOperand(0, in);
+    }
+
+    MToInt32 *mir() const {
+        return mir_->toToInt32();
+    }
+};
 
 // Convert a double to a truncated int32.
 //   Input: floating-point register
 //   Output: 32-bit integer
 class LTruncateDToInt32 : public LInstructionHelper<1, 1, 1>
 {
   public:
     LIR_HEADER(TruncateDToInt32)
--- a/js/src/jit/LOpcodes.h
+++ b/js/src/jit/LOpcodes.h
@@ -122,16 +122,17 @@
     _(Int32ToDouble)                \
     _(Float32ToDouble)              \
     _(DoubleToFloat32)              \
     _(Int32ToFloat32)               \
     _(ValueToDouble)                \
     _(ValueToInt32)                 \
     _(ValueToFloat32)               \
     _(DoubleToInt32)                \
+    _(Float32ToInt32)               \
     _(TruncateDToInt32)             \
     _(IntToString)                  \
     _(DoubleToString)               \
     _(Start)                        \
     _(OsrEntry)                     \
     _(OsrValue)                     \
     _(OsrScopeChain)                \
     _(OsrArgumentsObject)           \
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -1665,16 +1665,22 @@ LIRGenerator::visitToInt32(MToInt32 *con
 
       case MIRType_Null:
         return define(new LInteger(0), convert);
 
       case MIRType_Int32:
       case MIRType_Boolean:
         return redefine(convert, opd);
 
+      case MIRType_Float32:
+      {
+        LFloat32ToInt32 *lir = new LFloat32ToInt32(useRegister(opd));
+        return assignSnapshot(lir) && define(lir, convert);
+      }
+
       case MIRType_Double:
       {
         LDoubleToInt32 *lir = new LDoubleToInt32(useRegister(opd));
         return assignSnapshot(lir) && define(lir, convert);
       }
 
       case MIRType_String:
         // Strings are complicated - we don't handle them yet.
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -2844,19 +2844,17 @@ class MAsmJSUnsignedToDouble
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
 
 // Converts a primitive (either typed or untyped) to an int32. If the input is
 // not primitive at runtime, a bailout occurs. If the input cannot be converted
 // to an int32 without loss (i.e. "5.5" or undefined) then a bailout occurs.
-class MToInt32
-  : public MUnaryInstruction,
-    public NoFloatPolicy<0>
+class MToInt32 : public MUnaryInstruction
 {
     bool canBeNegativeZero_;
 
     MToInt32(MDefinition *def)
       : MUnaryInstruction(def),
         canBeNegativeZero_(true)
     {
         setResultType(MIRType_Int32);
@@ -2886,19 +2884,19 @@ class MToInt32
         return congruentIfOperandsEqual(ins);
     }
 
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
     void computeRange();
 
-    TypePolicy *typePolicy() {
-        return this;
-    }
+#ifdef DEBUG
+    bool isConsistentFloat32Use() const { return true; }
+#endif
 };
 
 // Converts a value or typed input to a truncated int32, for use with bitwise
 // operations. This is an infallible ValueToECMAInt32.
 class MTruncateToInt32 : public MUnaryInstruction
 {
     MTruncateToInt32(MDefinition *def)
       : MUnaryInstruction(def)
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -111,16 +111,44 @@ MacroAssemblerARM::convertDoubleToInt32(
         // Move the top word of the double into the output reg, if it is non-zero,
         // then the original value was -0.0
         as_vxfer(dest, InvalidReg, src, FloatToCore, Assembler::Equal, 1);
         ma_cmp(dest, Imm32(0x80000000), Assembler::Equal);
         ma_b(fail, Assembler::Equal);
     }
 }
 
+// Checks whether a float32 is representable as a 32-bit integer. If so, the
+// integer is written to the output register. Otherwise, a bailout is taken to
+// the given snapshot. This function overwrites the scratch float register.
+void
+MacroAssemblerARM::convertFloat32ToInt32(const FloatRegister &src, const Register &dest, Label *fail, bool negativeZeroCheck)
+{
+    // convert the floating point value to an integer, if it did not fit,
+    //     then when we convert it *back* to  a float, it will have a
+    //     different value, which we can test.
+    ma_vcvt_F32_I32(src, ScratchFloatReg);
+    // move the value into the dest register.
+    ma_vxfer(ScratchFloatReg, dest);
+    ma_vcvt_I32_F32(ScratchFloatReg, ScratchFloatReg);
+    ma_vcmp_f32(src, ScratchFloatReg);
+    as_vmrs(pc);
+    ma_b(fail, Assembler::VFP_NotEqualOrUnordered);
+
+    if (negativeZeroCheck) {
+        ma_cmp(dest, Imm32(0));
+        // Test and bail for -0.0, when integer result is 0
+        // Move the top word of the double into the output reg, if it is non-zero,
+        // then the original value was -0.0
+        as_vxfer(dest, InvalidReg, src, FloatToCore, Assembler::Equal, 0);
+        ma_cmp(dest, Imm32(0x80000000), Assembler::Equal);
+        ma_b(fail, Assembler::Equal);
+    }
+}
+
 void
 MacroAssemblerARM::convertFloatToDouble(const FloatRegister &src, const FloatRegister &dest) {
     as_vcvt(VFPRegister(dest), VFPRegister(src).singleOverlay());
 }
 
 void
 MacroAssemblerARM::branchTruncateFloat32(const FloatRegister &src, const Register &dest, Label *fail) {
     ma_vcvt_F32_I32(src, ScratchFloatReg);
@@ -1481,16 +1509,21 @@ MacroAssemblerARM::ma_vimm_f32(float val
 }
 
 void
 MacroAssemblerARM::ma_vcmp(FloatRegister src1, FloatRegister src2, Condition cc)
 {
     as_vcmp(VFPRegister(src1), VFPRegister(src2), cc);
 }
 void
+MacroAssemblerARM::ma_vcmp_f32(FloatRegister src1, FloatRegister src2, Condition cc)
+{
+    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_vcvt_F64_I32(FloatRegister src, FloatRegister dest, Condition cc)
 {
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -48,16 +48,18 @@ class MacroAssemblerARM : public Assembl
     void convertBoolToInt32(Register source, Register dest);
     void convertInt32ToDouble(const Register &src, const FloatRegister &dest);
     void convertInt32ToDouble(const Address &src, FloatRegister dest);
     void convertUInt32ToDouble(const Register &src, const FloatRegister &dest);
     void convertDoubleToFloat(const FloatRegister &src, const FloatRegister &dest);
     void branchTruncateDouble(const FloatRegister &src, const Register &dest, Label *fail);
     void convertDoubleToInt32(const FloatRegister &src, const Register &dest, Label *fail,
                               bool negativeZeroCheck = true);
+    void convertFloat32ToInt32(const FloatRegister &src, const Register &dest, Label *fail,
+                               bool negativeZeroCheck = true);
 
     void convertFloatToDouble(const FloatRegister &src, const FloatRegister &dest);
     void branchTruncateFloat32(const FloatRegister &src, const Register &dest, Label *fail);
     void convertInt32ToFloat32(const Register &src, const FloatRegister &dest);
     void convertInt32ToFloat32(const Address &src, FloatRegister dest);
 
     void addDouble(FloatRegister src, FloatRegister dest);
     void subDouble(FloatRegister src, FloatRegister dest);
@@ -317,16 +319,17 @@ class MacroAssemblerARM : public Assembl
     void ma_vabs(FloatRegister src, FloatRegister dest, Condition cc = Always);
 
     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_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);
 
--- a/js/src/jit/shared/Assembler-x86-shared.h
+++ b/js/src/jit/shared/Assembler-x86-shared.h
@@ -1298,16 +1298,20 @@ class AssemblerX86Shared
     void cvtsi2sd(const Register &src, const FloatRegister &dest) {
         JS_ASSERT(HasSSE2());
         masm.cvtsi2sd_rr(src.code(), dest.code());
     }
     void movmskpd(const FloatRegister &src, const Register &dest) {
         JS_ASSERT(HasSSE2());
         masm.movmskpd_rr(src.code(), dest.code());
     }
+    void movmskps(const FloatRegister &src, const Register &dest) {
+        JS_ASSERT(HasSSE2());
+        masm.movmskps_rr(src.code(), dest.code());
+    }
     void ptest(const FloatRegister &lhs, const FloatRegister &rhs) {
         JS_ASSERT(HasSSE41());
         masm.ptest_rr(rhs.code(), lhs.code());
     }
     void ucomisd(const FloatRegister &lhs, const FloatRegister &rhs) {
         JS_ASSERT(HasSSE2());
         masm.ucomisd_rr(rhs.code(), lhs.code());
     }
--- a/js/src/jit/shared/MacroAssembler-x86-shared.h
+++ b/js/src/jit/shared/MacroAssembler-x86-shared.h
@@ -452,16 +452,48 @@ class MacroAssemblerX86Shared : public A
                 andl(Imm32(1), dest);
                 j(Assembler::NonZero, fail);
             }
 
             bind(&notZero);
         }
     }
 
+    // Checks whether a float32 is representable as a 32-bit integer. If so, the
+    // integer is written to the output register. Otherwise, a bailout is taken to
+    // the given snapshot. This function overwrites the scratch float register.
+    void convertFloat32ToInt32(FloatRegister src, Register dest, Label *fail,
+                               bool negativeZeroCheck = true)
+    {
+        cvttss2si(src, dest);
+        convertInt32ToFloat32(dest, ScratchFloatReg);
+        ucomiss(src, ScratchFloatReg);
+        j(Assembler::Parity, fail);
+        j(Assembler::NotEqual, fail);
+
+        // Check for -0
+        if (negativeZeroCheck) {
+            Label notZero;
+            branchTest32(Assembler::NonZero, dest, dest, &notZero);
+
+            if (Assembler::HasSSE41()) {
+                ptest(src, src);
+                j(Assembler::NonZero, fail);
+            } else {
+                // bit 0 = sign of low float
+                // bits 1 to 3 = signs of higher floats
+                movmskps(src, dest);
+                andl(Imm32(1), dest);
+                j(Assembler::NonZero, fail);
+            }
+
+            bind(&notZero);
+        }
+    }
+
     void clampIntToUint8(Register reg) {
         Label inRange;
         branchTest32(Assembler::Zero, reg, Imm32(0xffffff00), &inRange);
         {
             sarl(Imm32(31), reg);
             notl(reg);
             andl(Imm32(255), reg);
         }