Bug 913282: More Float32 operators: TruncateToInt32; p=dougc,bbouvier, r=jonco,jandem
authorBenjamin Bouvier <benj@benj.me>
Thu, 18 Jul 2013 16:45:16 -0700
changeset 166443 77b72ea510d0b8bb05f1d039db6d85c9d103d225
parent 166442 9916cc731d15ebfdaf2714c51c40a402294044f1
child 166444 03fdc9e5eea382176c6be74b6fbef9cc6822e16e
push id428
push userbbajaj@mozilla.com
push dateTue, 28 Jan 2014 00:16:25 +0000
treeherdermozilla-release@cd72a7ff3a75 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjonco, jandem
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: TruncateToInt32; p=dougc,bbouvier, r=jonco,jandem dougc for the ARM parts, bbouvier for the rest.
js/src/assembler/assembler/X86Assembler.h
js/src/jit/LIR-Common.h
js/src/jit/LOpcodes.h
js/src/jit/Lowering.cpp
js/src/jit/MCallOptimize.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/Lowering-arm.cpp
js/src/jit/arm/Lowering-arm.h
js/src/jit/arm/MacroAssembler-arm.h
js/src/jit/shared/CodeGenerator-shared.cpp
js/src/jit/shared/CodeGenerator-shared.h
js/src/jit/shared/Lowering-x86-shared.cpp
js/src/jit/shared/Lowering-x86-shared.h
js/src/jit/x64/Assembler-x64.h
js/src/jit/x64/CodeGenerator-x64.cpp
js/src/jit/x64/CodeGenerator-x64.h
js/src/jit/x64/MacroAssembler-x64.h
js/src/jit/x86/Assembler-x86.h
js/src/jit/x86/CodeGenerator-x86.cpp
js/src/jit/x86/CodeGenerator-x86.h
--- a/js/src/assembler/assembler/X86Assembler.h
+++ b/js/src/assembler/assembler/X86Assembler.h
@@ -264,17 +264,18 @@ private:
         OP_GROUP2_EvIb                  = 0xC1,
         OP_RET_Iz                       = 0xC2,
         OP_RET                          = 0xC3,
         OP_GROUP11_EvIb                 = 0xC6,
         OP_GROUP11_EvIz                 = 0xC7,
         OP_INT3                         = 0xCC,
         OP_GROUP2_Ev1                   = 0xD1,
         OP_GROUP2_EvCL                  = 0xD3,
-	OP_FPU6				= 0xDD,
+        OP_FPU6                         = 0xDD,
+        OP_FLD32                        = 0xD9,
         OP_CALL_rel32                   = 0xE8,
         OP_JMP_rel32                    = 0xE9,
         PRE_SSE_F2                      = 0xF2,
         PRE_SSE_F3                      = 0xF3,
         OP_HLT                          = 0xF4,
         OP_GROUP3_EbIb                  = 0xF6,
         OP_GROUP3_Ev                    = 0xF7,
         OP_GROUP3_EvIz                  = 0xF7, // OP_GROUP3_Ev has an immediate, when instruction is a test.
@@ -718,16 +719,21 @@ public:
     }
 #endif
 
     void fld_m(int offset, RegisterID base)
     {
         spew("fld        %s0x%x(%s)", PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.oneByteOp(OP_FPU6, FPU6_OP_FLD, base, offset);
     }
+    void fld32_m(int offset, RegisterID base)
+    {
+        spew("fld        %s0x%x(%s)", PRETTY_PRINT_OFFSET(offset), nameIReg(base));
+        m_formatter.oneByteOp(OP_FLD32, FPU6_OP_FLD, base, offset);
+    }
     void fisttp_m(int offset, RegisterID base)
     {
         spew("fisttp     %s0x%x(%s)", PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.oneByteOp(OP_FPU6, FPU6_OP_FISTTP, base, offset);
     }
     void fstp_m(int offset, RegisterID base)
     {
         spew("fstp       %s0x%x(%s)", PRETTY_PRINT_OFFSET(offset), nameIReg(base));
@@ -2403,24 +2409,29 @@ public:
              nameFPReg(src), nameIReg(4, dst));
         m_formatter.prefix(PRE_SSE_F3);
         m_formatter.twoByteOp(OP2_CVTTSD2SI_GdWsd, dst, (RegisterID)src);
     }
 
 #if WTF_CPU_X86_64
     void cvttsd2sq_rr(XMMRegisterID src, RegisterID dst)
     {
-        // We call this instruction cvttsd2sq to differentiate the 64-bit
-        // version from the 32-bit version, but in assembler it's just
-        // called cvttsd2si and it's disambiguated by the register name.
         spew("cvttsd2si  %s, %s",
              nameFPReg(src), nameIReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp64(OP2_CVTTSD2SI_GdWsd, dst, (RegisterID)src);
     }
+
+    void cvttss2sq_rr(XMMRegisterID src, RegisterID dst)
+    {
+        spew("cvttss2si  %s, %s",
+             nameFPReg(src), nameIReg(dst));
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteOp64(OP2_CVTTSD2SI_GdWsd, dst, (RegisterID)src);
+    }
 #endif
 
     void unpcklps_rr(XMMRegisterID src, XMMRegisterID dst)
     {
         spew("unpcklps   %s, %s",
              nameFPReg(src), nameFPReg(dst));
         m_formatter.twoByteOp(OP2_UNPCKLPS_VsdWsd, (RegisterID)dst, (RegisterID)src);
     }
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -2769,16 +2769,34 @@ class LTruncateDToInt32 : public LInstru
         setTemp(0, temp);
     }
 
     const LDefinition *tempFloat() {
         return getTemp(0);
     }
 };
 
+// Convert a float32 to a truncated int32.
+//   Input: floating-point register
+//   Output: 32-bit integer
+class LTruncateFToInt32 : public LInstructionHelper<1, 1, 1>
+{
+  public:
+    LIR_HEADER(TruncateFToInt32)
+
+    LTruncateFToInt32(const LAllocation &in, const LDefinition &temp) {
+        setOperand(0, in);
+        setTemp(0, temp);
+    }
+
+    const LDefinition *tempFloat() {
+        return getTemp(0);
+    }
+};
+
 // Convert an integer hosted on one definition to a string with a function call.
 class LIntToString : public LInstructionHelper<1, 1, 0>
 {
   public:
     LIR_HEADER(IntToString)
 
     LIntToString(const LAllocation &input) {
         setOperand(0, input);
--- a/js/src/jit/LOpcodes.h
+++ b/js/src/jit/LOpcodes.h
@@ -125,16 +125,17 @@
     _(DoubleToFloat32)              \
     _(Int32ToFloat32)               \
     _(ValueToDouble)                \
     _(ValueToInt32)                 \
     _(ValueToFloat32)               \
     _(DoubleToInt32)                \
     _(Float32ToInt32)               \
     _(TruncateDToInt32)             \
+    _(TruncateFToInt32)             \
     _(IntToString)                  \
     _(DoubleToString)               \
     _(Start)                        \
     _(OsrEntry)                     \
     _(OsrValue)                     \
     _(OsrScopeChain)                \
     _(OsrArgumentsObject)           \
     _(RegExp)                       \
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -1731,16 +1731,19 @@ LIRGenerator::visitTruncateToInt32(MTrun
 
       case MIRType_Int32:
       case MIRType_Boolean:
         return redefine(truncate, opd);
 
       case MIRType_Double:
         return lowerTruncateDToInt32(truncate);
 
+      case MIRType_Float32:
+        return lowerTruncateFToInt32(truncate);
+
       default:
         // Objects might be effectful.
         // Strings are complicated - we don't handle them yet.
         MOZ_ASSUME_UNREACHABLE("unexpected type");
     }
 }
 
 bool
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -795,19 +795,19 @@ IonBuilder::inlineMathImul(CallInfo &cal
 {
     if (callInfo.argc() != 2 || callInfo.constructing())
         return InliningStatus_NotInlined;
 
     MIRType returnType = getInlineReturnType();
     if (returnType != MIRType_Int32)
         return InliningStatus_NotInlined;
 
-    if (!IsNumberType(callInfo.getArg(0)->type()) || callInfo.getArg(0)->type() == MIRType_Float32)
+    if (!IsNumberType(callInfo.getArg(0)->type()))
         return InliningStatus_NotInlined;
-    if (!IsNumberType(callInfo.getArg(1)->type()) || callInfo.getArg(1)->type() == MIRType_Float32)
+    if (!IsNumberType(callInfo.getArg(1)->type()))
         return InliningStatus_NotInlined;
 
     callInfo.unwrapArgs();
 
     MInstruction *first = MTruncateToInt32::New(callInfo.getArg(0));
     current->add(first);
 
     MInstruction *second = MTruncateToInt32::New(callInfo.getArg(1));
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -2948,16 +2948,21 @@ class MTruncateToInt32 : public MUnaryIn
         return congruentIfOperandsEqual(ins);
     }
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 
     void computeRange();
     bool isOperandTruncated(size_t index) const;
+# ifdef DEBUG
+    bool isConsistentFloat32Use() const {
+        return true;
+    }
+#endif
 };
 
 // Converts any type to a string
 class MToString : public MUnaryInstruction
 {
     MToString(MDefinition *def)
       : MUnaryInstruction(def)
     {
--- a/js/src/jit/TypePolicy.cpp
+++ b/js/src/jit/TypePolicy.cpp
@@ -335,23 +335,16 @@ BitwisePolicy::adjustInputs(MInstruction
         MDefinition *in = ins->getOperand(i);
         if (in->type() == MIRType_Int32)
             continue;
 
         // See BinaryArithPolicy::adjustInputs for an explanation of the following
         if (in->type() == MIRType_Object || in->type() == MIRType_String)
             in = boxAt(ins, in);
 
-        if (in->type() == MIRType_Float32) {
-            MToDouble *replace = MToDouble::New(in);
-            ins->block()->insertBefore(ins, replace);
-            ins->replaceOperand(i, replace);
-            in = replace;
-        }
-
         MInstruction *replace = MTruncateToInt32::New(in);
         ins->block()->insertBefore(ins, replace);
         ins->replaceOperand(i, replace);
     }
 
     return true;
 }
 
@@ -647,21 +640,16 @@ StoreTypedArrayPolicy::adjustValueInput(
     switch (arrayType) {
       case ScalarTypeRepresentation::TYPE_INT8:
       case ScalarTypeRepresentation::TYPE_UINT8:
       case ScalarTypeRepresentation::TYPE_INT16:
       case ScalarTypeRepresentation::TYPE_UINT16:
       case ScalarTypeRepresentation::TYPE_INT32:
       case ScalarTypeRepresentation::TYPE_UINT32:
         if (value->type() != MIRType_Int32) {
-            // Workaround for bug 915903
-            if (value->type() == MIRType_Float32) {
-                value = MToDouble::New(value);
-                ins->block()->insertBefore(ins, value->toInstruction());
-            }
             value = MTruncateToInt32::New(value);
             ins->block()->insertBefore(ins, value->toInstruction());
         }
         break;
       case ScalarTypeRepresentation::TYPE_UINT8_CLAMPED:
         // IonBuilder should have inserted ClampToUint8.
         JS_ASSERT(value->type() == MIRType_Int32);
         break;
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -1208,16 +1208,22 @@ CodeGeneratorARM::emitRoundDouble(const 
 }
 
 bool
 CodeGeneratorARM::visitTruncateDToInt32(LTruncateDToInt32 *ins)
 {
     return emitTruncateDouble(ToFloatRegister(ins->input()), ToRegister(ins->output()));
 }
 
+bool
+CodeGeneratorARM::visitTruncateFToInt32(LTruncateFToInt32 *ins)
+{
+    return emitTruncateFloat32(ToFloatRegister(ins->input()), ToRegister(ins->output()));
+}
+
 static const uint32_t FrameSizes[] = { 128, 256, 512, 1024 };
 
 FrameSizeClass
 FrameSizeClass::FromDepth(uint32_t frameDepth)
 {
     for (uint32_t i = 0; i < JS_ARRAY_LENGTH(FrameSizes); i++) {
         if (frameDepth < FrameSizes[i])
             return FrameSizeClass(i);
--- a/js/src/jit/arm/CodeGenerator-arm.h
+++ b/js/src/jit/arm/CodeGenerator-arm.h
@@ -100,16 +100,17 @@ class CodeGeneratorARM : public CodeGene
     virtual bool visitNotI(LNotI *ins);
     virtual bool visitNotD(LNotD *ins);
 
     virtual bool visitMathD(LMathD *math);
     virtual bool visitMathF(LMathF *math);
     virtual bool visitFloor(LFloor *lir);
     virtual bool visitRound(LRound *lir);
     virtual bool visitTruncateDToInt32(LTruncateDToInt32 *ins);
+    virtual bool visitTruncateFToInt32(LTruncateFToInt32 *ins);
 
     // Out of line visitors.
     bool visitOutOfLineBailout(OutOfLineBailout *ool);
     bool visitOutOfLineTableSwitch(OutOfLineTableSwitch *ool);
 
   protected:
     ValueOperand ToValue(LInstruction *ins, size_t pos);
     ValueOperand ToOutValue(LInstruction *ins);
--- a/js/src/jit/arm/Lowering-arm.cpp
+++ b/js/src/jit/arm/Lowering-arm.cpp
@@ -517,14 +517,23 @@ LIRGeneratorARM::lowerTruncateDToInt32(M
 {
     MDefinition *opd = ins->input();
     JS_ASSERT(opd->type() == MIRType_Double);
 
     return define(new LTruncateDToInt32(useRegister(opd), LDefinition::BogusTemp()), ins);
 }
 
 bool
+LIRGeneratorARM::lowerTruncateFToInt32(MTruncateToInt32 *ins)
+{
+    MDefinition *opd = ins->input();
+    JS_ASSERT(opd->type() == MIRType_Float32);
+
+    return define(new LTruncateFToInt32(useRegister(opd), LDefinition::BogusTemp()), ins);
+}
+
+bool
 LIRGeneratorARM::visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins)
 {
     MOZ_ASSUME_UNREACHABLE("NYI");
 }
 
 //__aeabi_uidiv
--- a/js/src/jit/arm/Lowering-arm.h
+++ b/js/src/jit/arm/Lowering-arm.h
@@ -56,16 +56,17 @@ class LIRGeneratorARM : public LIRGenera
                      MDefinition *src);
     bool lowerForFPU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir,
                      MDefinition *lhs, MDefinition *rhs);
     bool lowerForBitAndAndBranch(LBitAndAndBranch *baab, MInstruction *mir,
                                  MDefinition *lhs, MDefinition *rhs);
     bool lowerConstantDouble(double d, MInstruction *ins);
     bool lowerConstantFloat32(float d, MInstruction *ins);
     bool lowerTruncateDToInt32(MTruncateToInt32 *ins);
+    bool lowerTruncateFToInt32(MTruncateToInt32 *ins);
     bool lowerDivI(MDiv *div);
     bool lowerModI(MMod *mod);
     bool lowerMulI(MMul *mul, MDefinition *lhs, MDefinition *rhs);
     bool lowerUDiv(MInstruction *div);
     bool lowerUMod(MInstruction *mod);
     bool visitPowHalf(MPowHalf *ins);
     bool visitAsmJSNeg(MAsmJSNeg *ins);
     bool visitAsmJSUDiv(MAsmJSUDiv *ins);
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -480,17 +480,18 @@ class MacroAssemblerARMCompat : public M
         setFramePushed(framePushed_ + value);
     }
   public:
     typedef MoveResolver::MoveOperand MoveOperand;
     typedef MoveResolver::Move Move;
 
     enum Result {
         GENERAL,
-        DOUBLE
+        DOUBLE,
+        FLOAT
     };
 
     MacroAssemblerARMCompat()
       : inCall_(false),
         enoughMemory_(true),
         framePushed_(0)
     { }
     bool oom() const {
@@ -600,31 +601,37 @@ class MacroAssemblerARMCompat : public M
     }
     void push(const Address &address) {
         ma_ldr(Operand(address.base, address.offset), ScratchRegister);
         ma_push(ScratchRegister);
     }
     void push(const Register &reg) {
         ma_push(reg);
     }
+    void push(const FloatRegister &reg) {
+        ma_vpush(VFPRegister(reg));
+    }
     void pushWithPadding(const Register &reg, const Imm32 extraSpace) {
         Imm32 totSpace = Imm32(extraSpace.value + 4);
         ma_dtr(IsStore, sp, totSpace, reg, PreIndex);
     }
     void pushWithPadding(const Imm32 &imm, const Imm32 extraSpace) {
         Imm32 totSpace = Imm32(extraSpace.value + 4);
         // ma_dtr may need the scratch register to adjust the stack, so use the
         // second scratch register.
         ma_mov(imm, secondScratchReg_);
         ma_dtr(IsStore, sp, totSpace, secondScratchReg_, PreIndex);
     }
 
     void pop(const Register &reg) {
         ma_pop(reg);
     }
+    void pop(const FloatRegister &reg) {
+        ma_vpop(VFPRegister(reg));
+    }
 
     void popN(const Register &reg, Imm32 extraSpace) {
         Imm32 totSpace = Imm32(extraSpace.value + 4);
         ma_dtr(IsLoad, sp, totSpace, reg, PostIndex);
     }
 
     CodeOffsetLabel toggledJump(Label *label);
 
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -671,31 +671,36 @@ CodeGeneratorShared::callVM(const VMFunc
     //    ... frame ...
     return true;
 }
 
 class OutOfLineTruncateSlow : public OutOfLineCodeBase<CodeGeneratorShared>
 {
     FloatRegister src_;
     Register dest_;
+    bool needFloat32Conversion_;
 
   public:
-    OutOfLineTruncateSlow(FloatRegister src, Register dest)
-      : src_(src), dest_(dest)
+    OutOfLineTruncateSlow(FloatRegister src, Register dest, bool needFloat32Conversion = false)
+      : src_(src), dest_(dest), needFloat32Conversion_(needFloat32Conversion)
     { }
 
     bool accept(CodeGeneratorShared *codegen) {
         return codegen->visitOutOfLineTruncateSlow(this);
     }
     FloatRegister src() const {
         return src_;
     }
     Register dest() const {
         return dest_;
     }
+    bool needFloat32Conversion() const {
+        return needFloat32Conversion_;
+    }
+
 };
 
 OutOfLineCode *
 CodeGeneratorShared::oolTruncateDouble(const FloatRegister &src, const Register &dest)
 {
     OutOfLineTruncateSlow *ool = new OutOfLineTruncateSlow(src, dest);
     if (!addOutOfLineCode(ool))
         return nullptr;
@@ -710,33 +715,53 @@ CodeGeneratorShared::emitTruncateDouble(
         return false;
 
     masm.branchTruncateDouble(src, dest, ool->entry());
     masm.bind(ool->rejoin());
     return true;
 }
 
 bool
+CodeGeneratorShared::emitTruncateFloat32(const FloatRegister &src, const Register &dest)
+{
+    OutOfLineTruncateSlow *ool = new OutOfLineTruncateSlow(src, dest, true);
+    if (!addOutOfLineCode(ool))
+        return false;
+
+    masm.branchTruncateFloat32(src, dest, ool->entry());
+    masm.bind(ool->rejoin());
+    return true;
+}
+
+bool
 CodeGeneratorShared::visitOutOfLineTruncateSlow(OutOfLineTruncateSlow *ool)
 {
     FloatRegister src = ool->src();
     Register dest = ool->dest();
 
     saveVolatile(dest);
 
+    if (ool->needFloat32Conversion()) {
+        masm.push(src);
+        masm.convertFloatToDouble(src, src);
+    }
+
     masm.setupUnalignedABICall(1, dest);
     masm.passABIArg(src);
     if (gen->compilingAsmJS())
         masm.callWithABI(AsmJSImm_ToInt32);
     else
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, js::ToInt32));
     masm.storeCallResult(dest);
 
     restoreVolatile(dest);
 
+    if (ool->needFloat32Conversion())
+        masm.pop(src);
+
     masm.jump(ool->rejoin());
     return true;
 }
 
 void
 CodeGeneratorShared::emitPreBarrier(Register base, const LAllocation *index, MIRType type)
 {
     if (index->isConstant()) {
--- a/js/src/jit/shared/CodeGenerator-shared.h
+++ b/js/src/jit/shared/CodeGenerator-shared.h
@@ -280,16 +280,17 @@ class CodeGeneratorShared : public LInst
     // current instruction, such that:
     //  (1) Invalidation will not overwrite the current instruction, and
     //  (2) Overwriting the current instruction will not overwrite
     //      an invalidation marker.
     void ensureOsiSpace();
 
     OutOfLineCode *oolTruncateDouble(const FloatRegister &src, const Register &dest);
     bool emitTruncateDouble(const FloatRegister &src, const Register &dest);
+    bool emitTruncateFloat32(const FloatRegister &src, const Register &dest);
 
     void emitPreBarrier(Register base, const LAllocation *index, MIRType type);
     void emitPreBarrier(Address address, MIRType type);
 
     inline bool isNextBlock(LBlock *block) {
         return (current->mir()->id() + 1 == block->mir()->id());
     }
 
--- a/js/src/jit/shared/Lowering-x86-shared.cpp
+++ b/js/src/jit/shared/Lowering-x86-shared.cpp
@@ -289,8 +289,18 @@ bool
 LIRGeneratorX86Shared::lowerTruncateDToInt32(MTruncateToInt32 *ins)
 {
     MDefinition *opd = ins->input();
     JS_ASSERT(opd->type() == MIRType_Double);
 
     LDefinition maybeTemp = Assembler::HasSSE3() ? LDefinition::BogusTemp() : tempFloat();
     return define(new LTruncateDToInt32(useRegister(opd), maybeTemp), ins);
 }
+
+bool
+LIRGeneratorX86Shared::lowerTruncateFToInt32(MTruncateToInt32 *ins)
+{
+    MDefinition *opd = ins->input();
+    JS_ASSERT(opd->type() == MIRType_Float32);
+
+    LDefinition maybeTemp = Assembler::HasSSE3() ? LDefinition::BogusTemp() : tempFloat();
+    return define(new LTruncateFToInt32(useRegister(opd), maybeTemp), ins);
+}
--- a/js/src/jit/shared/Lowering-x86-shared.h
+++ b/js/src/jit/shared/Lowering-x86-shared.h
@@ -43,14 +43,15 @@ class LIRGeneratorX86Shared : public LIR
     bool lowerDivI(MDiv *div);
     bool lowerModI(MMod *mod);
     bool lowerUDiv(MInstruction *div);
     bool lowerUMod(MInstruction *mod);
     bool lowerUrshD(MUrsh *mir);
     bool lowerConstantDouble(double d, MInstruction *ins);
     bool lowerConstantFloat32(float d, MInstruction *ins);
     bool lowerTruncateDToInt32(MTruncateToInt32 *ins);
+    bool lowerTruncateFToInt32(MTruncateToInt32 *ins);
 };
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_shared_Lowering_x86_shared_h */
--- a/js/src/jit/x64/Assembler-x64.h
+++ b/js/src/jit/x64/Assembler-x64.h
@@ -660,16 +660,19 @@ class Assembler : public AssemblerX86Sha
     }
 
     // Do not mask shared implementations.
     using AssemblerX86Shared::call;
 
     void cvttsd2sq(const FloatRegister &src, const Register &dest) {
         masm.cvttsd2sq_rr(src.code(), dest.code());
     }
+    void cvttss2sq(const FloatRegister &src, const Register &dest) {
+        masm.cvttss2sq_rr(src.code(), dest.code());
+    }
     void cvtsq2sd(const Register &src, const FloatRegister &dest) {
         masm.cvtsq2sd_rr(src.code(), dest.code());
     }
 };
 
 static inline void
 PatchJump(CodeLocationJump jump, CodeLocationLabel label)
 {
--- a/js/src/jit/x64/CodeGenerator-x64.cpp
+++ b/js/src/jit/x64/CodeGenerator-x64.cpp
@@ -563,8 +563,20 @@ CodeGeneratorX64::visitTruncateDToInt32(
     FloatRegister input = ToFloatRegister(ins->input());
     Register output = ToRegister(ins->output());
 
     // On x64, branchTruncateDouble uses cvttsd2sq. Unlike the x86
     // implementation, this should handle most doubles and we can just
     // call a stub if it fails.
     return emitTruncateDouble(input, output);
 }
+
+bool
+CodeGeneratorX64::visitTruncateFToInt32(LTruncateFToInt32 *ins)
+{
+    FloatRegister input = ToFloatRegister(ins->input());
+    Register output = ToRegister(ins->output());
+
+    // On x64, branchTruncateFloat32 uses cvttss2sq. Unlike the x86
+    // implementation, this should handle most floats and we can just
+    // call a stub if it fails.
+    return emitTruncateFloat32(input, output);
+}
--- a/js/src/jit/x64/CodeGenerator-x64.h
+++ b/js/src/jit/x64/CodeGenerator-x64.h
@@ -45,16 +45,17 @@ class CodeGeneratorX64 : public CodeGene
     bool visitLoadElementT(LLoadElementT *load);
     bool visitImplicitThis(LImplicitThis *lir);
     bool visitInterruptCheck(LInterruptCheck *lir);
     bool visitCompareB(LCompareB *lir);
     bool visitCompareBAndBranch(LCompareBAndBranch *lir);
     bool visitCompareV(LCompareV *lir);
     bool visitCompareVAndBranch(LCompareVAndBranch *lir);
     bool visitTruncateDToInt32(LTruncateDToInt32 *ins);
+    bool visitTruncateFToInt32(LTruncateFToInt32 *ins);
     bool visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic *ins);
     bool visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic *ins);
     bool visitAsmJSLoadHeap(LAsmJSLoadHeap *ins);
     bool visitAsmJSStoreHeap(LAsmJSStoreHeap *ins);
     bool visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar *ins);
     bool visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar *ins);
     bool visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr *ins);
     bool visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc *ins);
--- a/js/src/jit/x64/MacroAssembler-x64.h
+++ b/js/src/jit/x64/MacroAssembler-x64.h
@@ -1045,16 +1045,25 @@ class MacroAssemblerX64 : public MacroAs
         // cvttsd2sq returns 0x8000000000000000 on failure. Test for it by
         // subtracting 1 and testing overflow (this avoids the need to
         // materialize that value in a register).
         cmpq(dest, Imm32(1));
         j(Assembler::Overflow, fail);
 
         movl(dest, dest); // Zero upper 32-bits.
     }
+    void branchTruncateFloat32(const FloatRegister &src, const Register &dest, Label *fail) {
+        cvttss2sq(src, dest);
+
+        // Same trick as for Doubles
+        cmpq(dest, Imm32(1));
+        j(Assembler::Overflow, fail);
+
+        movl(dest, dest); // Zero upper 32-bits.
+    }
 
     Condition testInt32Truthy(bool truthy, const ValueOperand &operand) {
         testl(operand.valueReg(), operand.valueReg());
         return truthy ? NonZero : Zero;
     }
     void branchTestBooleanTruthy(bool truthy, const ValueOperand &operand, Label *label) {
         testl(operand.valueReg(), operand.valueReg());
         j(truthy ? NonZero : Zero, label);
--- a/js/src/jit/x86/Assembler-x86.h
+++ b/js/src/jit/x86/Assembler-x86.h
@@ -263,16 +263,26 @@ class Assembler : public AssemblerX86Sha
     }
     void xchg(const Register &src, const Register &dest) {
         xchgl(src, dest);
     }
     void lea(const Operand &src, const Register &dest) {
         return leal(src, dest);
     }
 
+    void fld32(const Operand &dest) {
+        switch (dest.kind()) {
+          case Operand::MEM_REG_DISP:
+            masm.fld32_m(dest.disp(), dest.base());
+            break;
+          default:
+            MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
+        }
+    }
+
     void cmpl(const Register src, ImmWord ptr) {
         masm.cmpl_ir(ptr.value, src.code());
     }
     void cmpl(const Register src, ImmPtr imm) {
         cmpl(src, ImmWord(uintptr_t(imm.value)));
     }
     void cmpl(const Register src, ImmGCPtr ptr) {
         masm.cmpl_ir(ptr.value, src.code());
--- a/js/src/jit/x86/CodeGenerator-x86.cpp
+++ b/js/src/jit/x86/CodeGenerator-x86.cpp
@@ -21,16 +21,19 @@
 #include "jit/shared/CodeGenerator-shared-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::DebugOnly;
 using mozilla::DoubleExponentBias;
 using mozilla::DoubleExponentShift;
+using mozilla::FloatExponentBias;
+using mozilla::FloatExponentShift;
+using mozilla::FloatExponentBits;
 using JS::GenericNaN;
 
 CodeGeneratorX86::CodeGeneratorX86(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm)
   : CodeGeneratorX86Shared(gen, graph, masm)
 {
 }
 
 static const uint32_t FrameSizes[] = { 128, 256, 512, 1024 };
@@ -785,16 +788,33 @@ class OutOfLineTruncate : public OutOfLi
     bool accept(CodeGeneratorX86 *codegen) {
         return codegen->visitOutOfLineTruncate(this);
     }
     LTruncateDToInt32 *ins() const {
         return ins_;
     }
 };
 
+class OutOfLineTruncateFloat32 : public OutOfLineCodeBase<CodeGeneratorX86>
+{
+    LTruncateFToInt32 *ins_;
+
+  public:
+    OutOfLineTruncateFloat32(LTruncateFToInt32 *ins)
+      : ins_(ins)
+    { }
+
+    bool accept(CodeGeneratorX86 *codegen) {
+        return codegen->visitOutOfLineTruncateFloat32(this);
+    }
+    LTruncateFToInt32 *ins() const {
+        return ins_;
+    }
+};
+
 } // namespace jit
 } // namespace js
 
 bool
 CodeGeneratorX86::visitTruncateDToInt32(LTruncateDToInt32 *ins)
 {
     FloatRegister input = ToFloatRegister(ins->input());
     Register output = ToRegister(ins->output());
@@ -804,16 +824,31 @@ CodeGeneratorX86::visitTruncateDToInt32(
         return false;
 
     masm.branchTruncateDouble(input, output, ool->entry());
     masm.bind(ool->rejoin());
     return true;
 }
 
 bool
+CodeGeneratorX86::visitTruncateFToInt32(LTruncateFToInt32 *ins)
+{
+    FloatRegister input = ToFloatRegister(ins->input());
+    Register output = ToRegister(ins->output());
+
+    OutOfLineTruncateFloat32 *ool = new OutOfLineTruncateFloat32(ins);
+    if (!addOutOfLineCode(ool))
+        return false;
+
+    masm.branchTruncateFloat32(input, output, ool->entry());
+    masm.bind(ool->rejoin());
+    return true;
+}
+
+bool
 CodeGeneratorX86::visitOutOfLineTruncate(OutOfLineTruncate *ool)
 {
     LTruncateDToInt32 *ins = ool->ins();
     FloatRegister input = ToFloatRegister(ins->input());
     Register output = ToRegister(ins->output());
 
     Label fail;
 
@@ -890,8 +925,97 @@ CodeGeneratorX86::visitOutOfLineTruncate
         masm.storeCallResult(output);
 
         restoreVolatile(output);
     }
 
     masm.jump(ool->rejoin());
     return true;
 }
+
+bool
+CodeGeneratorX86::visitOutOfLineTruncateFloat32(OutOfLineTruncateFloat32 *ool)
+{
+    LTruncateFToInt32 *ins = ool->ins();
+    FloatRegister input = ToFloatRegister(ins->input());
+    Register output = ToRegister(ins->output());
+
+    Label fail;
+
+    if (Assembler::HasSSE3()) {
+        // Push float32, but subtracts 64 bits so that the value popped by fisttp fits
+        masm.subl(Imm32(sizeof(uint64_t)), esp);
+        masm.storeFloat(input, Operand(esp, 0));
+
+        static const uint32_t EXPONENT_MASK = FloatExponentBits;
+        static const uint32_t EXPONENT_SHIFT = FloatExponentShift;
+        // Integers are still 64 bits long, so we can still test for an exponent > 63.
+        static const uint32_t TOO_BIG_EXPONENT = (FloatExponentBias + 63) << EXPONENT_SHIFT;
+
+        // Check exponent to avoid fp exceptions.
+        Label failPopFloat;
+        masm.movl(Operand(esp, 0), output);
+        masm.and32(Imm32(EXPONENT_MASK), output);
+        masm.branch32(Assembler::GreaterThanOrEqual, output, Imm32(TOO_BIG_EXPONENT), &failPopFloat);
+
+        // Load float, perform 32-bit truncation.
+        masm.fld32(Operand(esp, 0));
+        masm.fisttp(Operand(esp, 0));
+
+        // Load low word, pop 64bits and jump back.
+        masm.movl(Operand(esp, 0), output);
+        masm.addl(Imm32(sizeof(uint64_t)), esp);
+        masm.jump(ool->rejoin());
+
+        masm.bind(&failPopFloat);
+        masm.addl(Imm32(sizeof(uint64_t)), esp);
+        masm.jump(&fail);
+    } else {
+        FloatRegister temp = ToFloatRegister(ins->tempFloat());
+
+        // Try to convert float32 representing integers within 2^32 of a signed
+        // integer, by adding/subtracting 2^32 and then trying to convert to int32.
+        // This has to be an exact conversion, as otherwise the truncation works
+        // incorrectly on the modified value.
+        masm.xorps(ScratchFloatReg, ScratchFloatReg);
+        masm.ucomiss(input, ScratchFloatReg);
+        masm.j(Assembler::Parity, &fail);
+
+        {
+            Label positive;
+            masm.j(Assembler::Above, &positive);
+
+            masm.loadConstantFloat32(4294967296.f, temp);
+            Label skip;
+            masm.jmp(&skip);
+
+            masm.bind(&positive);
+            masm.loadConstantFloat32(-4294967296.f, temp);
+            masm.bind(&skip);
+        }
+
+        masm.addss(input, temp);
+        masm.cvttss2si(temp, output);
+        masm.cvtsi2ss(output, ScratchFloatReg);
+
+        masm.ucomiss(temp, ScratchFloatReg);
+        masm.j(Assembler::Parity, &fail);
+        masm.j(Assembler::Equal, ool->rejoin());
+    }
+
+    masm.bind(&fail);
+    {
+        saveVolatile(output);
+
+        masm.push(input);
+        masm.setupUnalignedABICall(1, output);
+        masm.cvtss2sd(input, input);
+        masm.passABIArg(input);
+        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, js::ToInt32));
+        masm.storeCallResult(output);
+        masm.pop(input);
+
+        restoreVolatile(output);
+    }
+
+    masm.jump(ool->rejoin());
+    return true;
+}
--- a/js/src/jit/x86/CodeGenerator-x86.h
+++ b/js/src/jit/x86/CodeGenerator-x86.h
@@ -10,16 +10,17 @@
 #include "jit/shared/CodeGenerator-x86-shared.h"
 #include "jit/x86/Assembler-x86.h"
 
 namespace js {
 namespace jit {
 
 class OutOfLineLoadTypedArrayOutOfBounds;
 class OutOfLineTruncate;
+class OutOfLineTruncateFloat32;
 
 class CodeGeneratorX86 : public CodeGeneratorX86Shared
 {
   private:
     CodeGeneratorX86 *thisFromCtor() {
         return this;
     }
 
@@ -59,27 +60,29 @@ class CodeGeneratorX86 : public CodeGene
     bool visitImplicitThis(LImplicitThis *lir);
     bool visitInterruptCheck(LInterruptCheck *lir);
     bool visitCompareB(LCompareB *lir);
     bool visitCompareBAndBranch(LCompareBAndBranch *lir);
     bool visitCompareV(LCompareV *lir);
     bool visitCompareVAndBranch(LCompareVAndBranch *lir);
     bool visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble *lir);
     bool visitTruncateDToInt32(LTruncateDToInt32 *ins);
+    bool visitTruncateFToInt32(LTruncateFToInt32 *ins);
     bool visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic *ins);
     bool visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic *ins);
     bool visitAsmJSLoadHeap(LAsmJSLoadHeap *ins);
     bool visitAsmJSStoreHeap(LAsmJSStoreHeap *ins);
     bool visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar *ins);
     bool visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar *ins);
     bool visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr *ins);
     bool visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc *ins);
 
     bool visitOutOfLineLoadTypedArrayOutOfBounds(OutOfLineLoadTypedArrayOutOfBounds *ool);
     bool visitOutOfLineTruncate(OutOfLineTruncate *ool);
+    bool visitOutOfLineTruncateFloat32(OutOfLineTruncateFloat32 *ool);
 
     void postAsmJSCall(LAsmJSCall *lir);
 };
 
 typedef CodeGeneratorX86 CodeGeneratorSpecific;
 
 } // namespace jit
 } // namespace js