Bug 1279248 - Part 20: Implement the 64bit variant of WasmTruncate on x86, r=sunfish
authorHannes Verschore <hv1989@gmail.com>
Fri, 29 Jul 2016 16:53:44 +0200
changeset 347341 cb47a62a37a46a6081031a8d2fc78770ea4ba3e6
parent 347340 efe2a71423f74f59d35902972739ec206e6c9daa
child 347342 0c40b01a4cba292b08577a71d5305325468a9c03
push id6389
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:38:22 +0000
treeherdermozilla-beta@01d67bfe6c81 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssunfish
bugs1279248
milestone50.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 1279248 - Part 20: Implement the 64bit variant of WasmTruncate on x86, r=sunfish
js/src/asmjs/WasmBaselineCompile.cpp
js/src/jit/BaselineIC.cpp
js/src/jit/MacroAssembler-inl.h
js/src/jit/MacroAssembler.cpp
js/src/jit/MacroAssembler.h
js/src/jit/SharedIC.cpp
js/src/jit/arm/MacroAssembler-arm-inl.h
js/src/jit/arm64/MacroAssembler-arm64-inl.h
js/src/jit/mips-shared/MacroAssembler-mips-shared-inl.h
js/src/jit/mips32/MacroAssembler-mips32.cpp
js/src/jit/mips64/MacroAssembler-mips64.cpp
js/src/jit/shared/CodeGenerator-shared.cpp
js/src/jit/x64/MacroAssembler-x64-inl.h
js/src/jit/x86-shared/Assembler-x86-shared.h
js/src/jit/x86-shared/BaseAssembler-x86-shared.h
js/src/jit/x86-shared/Encoding-x86-shared.h
js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h
js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp
js/src/jit/x86/Assembler-x86.h
js/src/jit/x86/CodeGenerator-x86.cpp
js/src/jit/x86/CodeGenerator-x86.h
js/src/jit/x86/LIR-x86.h
js/src/jit/x86/LOpcodes-x86.h
js/src/jit/x86/Lowering-x86.cpp
js/src/jit/x86/MacroAssembler-x86-inl.h
--- a/js/src/asmjs/WasmBaselineCompile.cpp
+++ b/js/src/asmjs/WasmBaselineCompile.cpp
@@ -2574,29 +2574,29 @@ class BaseCompiler
 
     MOZ_MUST_USE
     bool truncateF32ToI32(RegF32 src, RegI32 dest) {
         OutOfLineCode* ool =
             addOutOfLineCode(new (alloc_) OutOfLineTruncateF32OrF64ToI32(AnyReg(src),
                                                                          dest));
         if (!ool)
             return false;
-        masm.branchTruncateFloat32(src.reg, dest.reg, ool->entry());
+        masm.branchTruncateFloat32ToInt32(src.reg, dest.reg, ool->entry());
         masm.bind(ool->rejoin());
         return true;
     }
 
     MOZ_MUST_USE
     bool truncateF64ToI32(RegF64 src, RegI32 dest) {
         OutOfLineCode* ool =
             addOutOfLineCode(new (alloc_) OutOfLineTruncateF32OrF64ToI32(AnyReg(src),
                                                                          dest));
         if (!ool)
             return false;
-        masm.branchTruncateDouble(src.reg, dest.reg, ool->entry());
+        masm.branchTruncateDoubleToInt32(src.reg, dest.reg, ool->entry());
         masm.bind(ool->rejoin());
         return true;
     }
 
 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
     class OutOfLineTruncateCheckF32OrF64ToI64 : public OutOfLineCode
     {
         AnyReg src;
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -3180,17 +3180,17 @@ StoreToTypedArray(JSContext* cx, MacroAs
         masm.jump(&done);
 
         // If the value is a double, truncate and jump back.
         // Else, jump to failure.
         masm.bind(&notInt32);
         if (cx->runtime()->jitSupportsFloatingPoint) {
             masm.branchTestDouble(Assembler::NotEqual, value, failure);
             masm.unboxDouble(value, FloatReg0);
-            masm.branchTruncateDouble(FloatReg0, scratch, failureModifiedScratch);
+            masm.branchTruncateDoubleMaybeModUint32(FloatReg0, scratch, failureModifiedScratch);
             masm.jump(&isInt32);
         } else {
             masm.jump(failure);
         }
     }
 
     masm.bind(&done);
 }
--- a/js/src/jit/MacroAssembler-inl.h
+++ b/js/src/jit/MacroAssembler-inl.h
@@ -546,16 +546,72 @@ MacroAssembler::branchTestNeedsIncrement
 void
 MacroAssembler::branchTestMagicValue(Condition cond, const ValueOperand& val, JSWhyMagic why,
                                      Label* label)
 {
     MOZ_ASSERT(cond == Equal || cond == NotEqual);
     branchTestValue(cond, val, MagicValue(why), label);
 }
 
+void
+MacroAssembler::branchDoubleNotInInt64Range(Address src, Register temp, Label* fail)
+{
+    // Tests if double is in [INT64_MIN; INT64_MAX] range
+    uint32_t EXPONENT_MASK = 0x7ff00000;
+    uint32_t EXPONENT_SHIFT = FloatingPoint<double>::kExponentShift - 32;
+    uint32_t TOO_BIG_EXPONENT = (FloatingPoint<double>::kExponentBias + 63) << EXPONENT_SHIFT;
+
+    load32(Address(src.base, src.offset + sizeof(int32_t)), temp);
+    and32(Imm32(EXPONENT_MASK), temp);
+    branch32(Assembler::GreaterThanOrEqual, temp, Imm32(TOO_BIG_EXPONENT), fail);
+}
+
+void
+MacroAssembler::branchDoubleNotInUInt64Range(Address src, Register temp, Label* fail)
+{
+    // Note: returns failure on -0.0
+    // Tests if double is in [0; UINT64_MAX] range
+    // Take the sign also in the equation. That way we can compare in one test?
+    uint32_t EXPONENT_MASK = 0xfff00000;
+    uint32_t EXPONENT_SHIFT = FloatingPoint<double>::kExponentShift - 32;
+    uint32_t TOO_BIG_EXPONENT = (FloatingPoint<double>::kExponentBias + 64) << EXPONENT_SHIFT;
+
+    load32(Address(src.base, src.offset + sizeof(int32_t)), temp);
+    and32(Imm32(EXPONENT_MASK), temp);
+    branch32(Assembler::AboveOrEqual, temp, Imm32(TOO_BIG_EXPONENT), fail);
+}
+
+void
+MacroAssembler::branchFloat32NotInInt64Range(Address src, Register temp, Label* fail)
+{
+    // Tests if float is in [INT64_MIN; INT64_MAX] range
+    uint32_t EXPONENT_MASK = 0x7f800000;
+    uint32_t EXPONENT_SHIFT = FloatingPoint<float>::kExponentShift;
+    uint32_t TOO_BIG_EXPONENT = (FloatingPoint<float>::kExponentBias + 63) << EXPONENT_SHIFT;
+
+    load32(src, temp);
+    and32(Imm32(EXPONENT_MASK), temp);
+    branch32(Assembler::GreaterThanOrEqual, temp, Imm32(TOO_BIG_EXPONENT), fail);
+}
+
+void
+MacroAssembler::branchFloat32NotInUInt64Range(Address src, Register temp, Label* fail)
+{
+    // Note: returns failure on -0.0
+    // Tests if float is in [0; UINT64_MAX] range
+    // Take the sign also in the equation. That way we can compare in one test?
+    uint32_t EXPONENT_MASK = 0xff800000;
+    uint32_t EXPONENT_SHIFT = FloatingPoint<float>::kExponentShift;
+    uint32_t TOO_BIG_EXPONENT = (FloatingPoint<float>::kExponentBias + 64) << EXPONENT_SHIFT;
+
+    load32(src, temp);
+    and32(Imm32(EXPONENT_MASK), temp);
+    branch32(Assembler::AboveOrEqual, temp, Imm32(TOO_BIG_EXPONENT), fail);
+}
+
 // ========================================================================
 // Canonicalization primitives.
 void
 MacroAssembler::canonicalizeFloat(FloatRegister reg)
 {
     Label notNaN;
     branchFloat(DoubleOrdered, reg, reg, &notNaN);
     loadConstantFloat32(float(JS::GenericNaN()), reg);
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -1967,17 +1967,17 @@ MacroAssembler::convertDoubleToInt(Float
                                    IntConversionBehavior behavior)
 {
     switch (behavior) {
       case IntConversion_Normal:
       case IntConversion_NegativeZeroCheck:
         convertDoubleToInt32(src, output, fail, behavior == IntConversion_NegativeZeroCheck);
         break;
       case IntConversion_Truncate:
-        branchTruncateDouble(src, output, truncateFail ? truncateFail : fail);
+        branchTruncateDoubleMaybeModUint32(src, output, truncateFail ? truncateFail : fail);
         break;
       case IntConversion_ClampToUint8:
         // Clamping clobbers the input register, so use a temp.
         moveDouble(src, temp);
         clampDoubleToUint8(temp, output);
         break;
     }
 }
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -32,16 +32,18 @@
 #include "jit/AtomicOp.h"
 #include "jit/IonInstrumentation.h"
 #include "jit/JitCompartment.h"
 #include "jit/VMFunctions.h"
 #include "vm/ProxyObject.h"
 #include "vm/Shape.h"
 #include "vm/UnboxedObject.h"
 
+using mozilla::FloatingPoint;
+
 // * How to read/write MacroAssembler method declarations:
 //
 // The following macros are made to avoid #ifdef around each method declarations
 // of the Macro Assembler, and they are also used as an hint on the location of
 // the implementations of each method.  For example, the following declaration
 //
 //   void Pop(FloatRegister t) DEFINED_ON(x86_shared, arm);
 //
@@ -992,23 +994,45 @@ class MacroAssembler : public MacroAssem
     void branchValueIsNurseryObject(Condition cond, ValueOperand value, Register temp, Label* label) PER_ARCH;
 
     // This function compares a Value (lhs) which is having a private pointer
     // boxed inside a js::Value, with a raw pointer (rhs).
     inline void branchPrivatePtr(Condition cond, const Address& lhs, Register rhs, Label* label) PER_ARCH;
 
     inline void branchFloat(DoubleCondition cond, FloatRegister lhs, FloatRegister rhs,
                             Label* label) PER_SHARED_ARCH;
-    inline void branchTruncateFloat32(FloatRegister src, Register dest, Label* fail)
+
+    // Truncate a double/float32 to int32 and when it doesn't fit an int32 it will jump to
+    // the failure label. This particular variant is allowed to return the value module 2**32,
+    // which isn't implemented on all architectures.
+    // E.g. the x64 variants will do this only in the int64_t range.
+    inline void branchTruncateFloat32MaybeModUint32(FloatRegister src, Register dest, Label* fail)
+        DEFINED_ON(arm, arm64, mips_shared, x86, x64);
+    inline void branchTruncateDoubleMaybeModUint32(FloatRegister src, Register dest, Label* fail)
+        DEFINED_ON(arm, arm64, mips_shared, x86, x64);
+
+    // Truncate a double/float32 to intptr and when it doesn't fit jump to the failure label.
+    inline void branchTruncateFloat32ToPtr(FloatRegister src, Register dest, Label* fail)
+        DEFINED_ON(x86, x64);
+    inline void branchTruncateDoubleToPtr(FloatRegister src, Register dest, Label* fail)
+        DEFINED_ON(x86, x64);
+
+    // Truncate a double/float32 to int32 and when it doesn't fit jump to the failure label.
+    inline void branchTruncateFloat32ToInt32(FloatRegister src, Register dest, Label* fail)
+        DEFINED_ON(arm, arm64, mips_shared, x86, x64);
+    inline void branchTruncateDoubleToInt32(FloatRegister src, Register dest, Label* fail)
         DEFINED_ON(arm, arm64, mips_shared, x86, x64);
 
     inline void branchDouble(DoubleCondition cond, FloatRegister lhs, FloatRegister rhs,
                              Label* label) PER_SHARED_ARCH;
-    inline void branchTruncateDouble(FloatRegister src, Register dest, Label* fail)
-        DEFINED_ON(arm, arm64, mips_shared, x86, x64);
+
+    inline void branchDoubleNotInInt64Range(Address src, Register temp, Label* fail);
+    inline void branchDoubleNotInUInt64Range(Address src, Register temp, Label* fail);
+    inline void branchFloat32NotInInt64Range(Address src, Register temp, Label* fail);
+    inline void branchFloat32NotInUInt64Range(Address src, Register temp, Label* fail);
 
     template <typename T>
     inline void branchAdd32(Condition cond, T src, Register dest, Label* label) PER_SHARED_ARCH;
     template <typename T>
     inline void branchSub32(Condition cond, T src, Register dest, Label* label) PER_SHARED_ARCH;
 
     inline void decBranchPtr(Condition cond, Register lhs, Imm32 rhs, Label* label) PER_SHARED_ARCH;
 
@@ -1257,16 +1281,33 @@ class MacroAssembler : public MacroAssem
 
     inline void storeFloat32x3(FloatRegister src, const Address& dest) PER_SHARED_ARCH;
     inline void storeFloat32x3(FloatRegister src, const BaseIndex& dest) PER_SHARED_ARCH;
 
     template <typename T>
     void storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T& dest,
                            MIRType slotType) PER_ARCH;
 
+  public:
+    // ========================================================================
+    // Truncate floating point.
+
+    // Undefined behaviour when truncation is outside Int64 range.
+    // Needs a temp register if SSE3 is not present.
+    inline void truncateFloat32ToInt64(Address src, Address dest, Register temp)
+        DEFINED_ON(x86_shared);
+    inline void truncateFloat32ToUInt64(Address src, Address dest, Register temp,
+                                        FloatRegister floatTemp)
+        DEFINED_ON(x86, x64);
+    inline void truncateDoubleToInt64(Address src, Address dest, Register temp)
+        DEFINED_ON(x86_shared);
+    inline void truncateDoubleToUInt64(Address src, Address dest, Register temp,
+                                       FloatRegister floatTemp)
+        DEFINED_ON(x86, x64);
+
     //}}} check_macroassembler_style
   public:
 
     // Emits a test of a value against all types in a TypeSet. A scratch
     // register is required.
     template <typename Source>
     void guardTypeSet(const Source& address, const TypeSet* types, BarrierKind kind, Register scratch, Label* miss);
 
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -1400,17 +1400,17 @@ ICBinaryArith_DoubleWithInt32::Compiler:
         masm.unboxDouble(R1, FloatReg0);
         scratchReg = R1.scratchReg();
     }
 
     // Truncate the double to an int32.
     {
         Label doneTruncate;
         Label truncateABICall;
-        masm.branchTruncateDouble(FloatReg0, scratchReg, &truncateABICall);
+        masm.branchTruncateDoubleMaybeModUint32(FloatReg0, scratchReg, &truncateABICall);
         masm.jump(&doneTruncate);
 
         masm.bind(&truncateABICall);
         masm.push(intReg);
         masm.setupUnalignedABICall(scratchReg);
         masm.passABIArg(FloatReg0, MoveOp::DOUBLE);
         masm.callWithABI(mozilla::BitwiseCast<void*, int32_t(*)(double)>(JS::ToInt32));
         masm.storeCallResult(scratchReg);
@@ -1553,17 +1553,17 @@ ICUnaryArith_Double::Compiler::generateS
         masm.negateDouble(FloatReg0);
         masm.boxDouble(FloatReg0, R0);
     } else {
         // Truncate the double to an int32.
         Register scratchReg = R1.scratchReg();
 
         Label doneTruncate;
         Label truncateABICall;
-        masm.branchTruncateDouble(FloatReg0, scratchReg, &truncateABICall);
+        masm.branchTruncateDoubleMaybeModUint32(FloatReg0, scratchReg, &truncateABICall);
         masm.jump(&doneTruncate);
 
         masm.bind(&truncateABICall);
         masm.setupUnalignedABICall(scratchReg);
         masm.passABIArg(FloatReg0, MoveOp::DOUBLE);
         masm.callWithABI(BitwiseCast<void*, int32_t(*)(double)>(JS::ToInt32));
         masm.storeCallResult(scratchReg);
 
--- a/js/src/jit/arm/MacroAssembler-arm-inl.h
+++ b/js/src/jit/arm/MacroAssembler-arm-inl.h
@@ -874,17 +874,23 @@ MacroAssembler::branchFloat(DoubleCondit
         ma_b(label, VFP_Equal);
         return;
     }
 
     ma_b(label, ConditionFromDoubleCondition(cond));
 }
 
 void
-MacroAssembler::branchTruncateFloat32(FloatRegister src, Register dest, Label* fail)
+MacroAssembler::branchTruncateFloat32MaybeModUint32(FloatRegister src, Register dest, Label* fail)
+{
+    branchTruncateFloat32ToInt32(src, dest, fail);
+}
+
+void
+MacroAssembler::branchTruncateFloat32ToInt32(FloatRegister src, Register dest, Label* fail)
 {
     ScratchFloat32Scope scratch(*this);
     ma_vcvt_F32_I32(src, scratch.sintOverlay());
     ma_vxfer(scratch, dest);
     ma_cmp(dest, Imm32(0x7fffffff));
     ma_cmp(dest, Imm32(0x80000000), Assembler::NotEqual);
     ma_b(fail, Assembler::Equal);
 }
@@ -908,26 +914,32 @@ MacroAssembler::branchDouble(DoubleCondi
         ma_b(label, VFP_Unordered);
         ma_b(label, VFP_Equal);
         return;
     }
 
     ma_b(label, ConditionFromDoubleCondition(cond));
 }
 
-// There are two options for implementing branchTruncateDouble:
+void
+MacroAssembler::branchTruncateDoubleMaybeModUint32(FloatRegister src, Register dest, Label* fail)
+{
+    branchTruncateDoubleToInt32(src, dest, fail);
+}
+
+// There are two options for implementing branchTruncateDoubleToInt32:
 //
 // 1. Convert the floating point value to an integer, if it did not fit, then it
 // was clamped to INT_MIN/INT_MAX, and we can test it. NOTE: if the value
 // really was supposed to be INT_MAX / INT_MIN then it will be wrong.
 //
 // 2. Convert the floating point value to an integer, if it did not fit, then it
 // set one or two bits in the fpcsr. Check those.
 void
-MacroAssembler::branchTruncateDouble(FloatRegister src, Register dest, Label* fail)
+MacroAssembler::branchTruncateDoubleToInt32(FloatRegister src, Register dest, Label* fail)
 {
     ScratchDoubleScope scratch(*this);
     FloatRegister scratchSIntReg = scratch.sintOverlay();
 
     ma_vcvt_F64_I32(src, scratchSIntReg);
     ma_vxfer(scratchSIntReg, dest);
     ma_cmp(dest, Imm32(0x7fffffff));
     ma_cmp(dest, Imm32(0x80000000), Assembler::NotEqual);
--- a/js/src/jit/arm64/MacroAssembler-arm64-inl.h
+++ b/js/src/jit/arm64/MacroAssembler-arm64-inl.h
@@ -913,17 +913,17 @@ MacroAssembler::branchFloat(DoubleCondit
         branch(Equal, label);
         break;
       default:
         branch(Condition(cond), label);
     }
 }
 
 void
-MacroAssembler::branchTruncateFloat32(FloatRegister src, Register dest, Label* fail)
+MacroAssembler::branchTruncateFloat32MaybeModUint32(FloatRegister src, Register dest, Label* fail)
 {
     vixl::UseScratchRegisterScope temps(this);
     const ARMRegister scratch64 = temps.AcquireX();
 
     ARMFPRegister src32(src, 32);
     ARMRegister dest64(dest, 64);
 
     MOZ_ASSERT(!scratch64.Is(dest64));
@@ -931,16 +931,22 @@ MacroAssembler::branchTruncateFloat32(Fl
     Fcvtzs(dest64, src32);
     Add(scratch64, dest64, Operand(0x7fffffffffffffff));
     Cmn(scratch64, 3);
     B(fail, Assembler::Above);
     And(dest64, dest64, Operand(0xffffffff));
 }
 
 void
+MacroAssembler::branchTruncateFloat32ToInt32(FloatRegister src, Register dest, Label* fail)
+{
+    convertFloat32ToInt32(src, dest, fail);
+}
+
+void
 MacroAssembler::branchDouble(DoubleCondition cond, FloatRegister lhs, FloatRegister rhs,
                              Label* label)
 {
     compareDouble(cond, lhs, rhs);
     switch (cond) {
       case DoubleNotEqual: {
         Label unordered;
         // not equal *and* ordered
@@ -954,17 +960,17 @@ MacroAssembler::branchDouble(DoubleCondi
         branch(Equal, label);
         break;
       default:
         branch(Condition(cond), label);
     }
 }
 
 void
-MacroAssembler::branchTruncateDouble(FloatRegister src, Register dest, Label* fail)
+MacroAssembler::branchTruncateDoubleMaybeModUint32(FloatRegister src, Register dest, Label* fail)
 {
     vixl::UseScratchRegisterScope temps(this);
     const ARMRegister scratch64 = temps.AcquireX();
 
     // An out of range integer will be saturated to the destination size.
     ARMFPRegister src64(src, 64);
     ARMRegister dest64(dest, 64);
 
@@ -972,16 +978,22 @@ MacroAssembler::branchTruncateDouble(Flo
 
     Fcvtzs(dest64, src64);
     Add(scratch64, dest64, Operand(0x7fffffffffffffff));
     Cmn(scratch64, 3);
     B(fail, Assembler::Above);
     And(dest64, dest64, Operand(0xffffffff));
 }
 
+void
+MacroAssembler::branchTruncateDoubleToInt32(FloatRegister src, Register dest, Label* fail)
+{
+    convertDoubleToInt32(src, dest, fail);
+}
+
 template <typename T>
 void
 MacroAssembler::branchAdd32(Condition cond, T src, Register dest, Label* label)
 {
     adds32(src, dest);
     branch(cond, label);
 }
 
--- a/js/src/jit/mips-shared/MacroAssembler-mips-shared-inl.h
+++ b/js/src/jit/mips-shared/MacroAssembler-mips-shared-inl.h
@@ -563,47 +563,59 @@ MacroAssembler::branchPtrWithPatch(Condi
 void
 MacroAssembler::branchFloat(DoubleCondition cond, FloatRegister lhs, FloatRegister rhs,
                             Label* label)
 {
     ma_bc1s(lhs, rhs, label, cond);
 }
 
 void
-MacroAssembler::branchTruncateFloat32(FloatRegister src, Register dest, Label* fail)
+MacroAssembler::branchTruncateFloat32MaybeModUint32(FloatRegister src, Register dest, Label* fail)
 {
     Label test, success;
     as_truncws(ScratchFloat32Reg, src);
     as_mfc1(dest, ScratchFloat32Reg);
 
     ma_b(dest, Imm32(INT32_MAX), fail, Assembler::Equal);
 }
 
 void
+MacroAssembler::branchTruncateFloat32ToInt32(FloatRegister src, Register dest, Label* fail)
+{
+    convertFloat32ToInt32(src, dest, fail);
+}
+
+void
 MacroAssembler::branchDouble(DoubleCondition cond, FloatRegister lhs, FloatRegister rhs,
                              Label* label)
 {
     ma_bc1d(lhs, rhs, label, cond);
 }
 
 // Convert the floating point value to an integer, if it did not fit, then it
 // was clamped to INT32_MIN/INT32_MAX, and we can test it.
 // NOTE: if the value really was supposed to be INT32_MAX / INT32_MIN then it
 // will be wrong.
 void
-MacroAssembler::branchTruncateDouble(FloatRegister src, Register dest, Label* fail)
+MacroAssembler::branchTruncateDoubleMaybeModUint32(FloatRegister src, Register dest, Label* fail)
 {
     Label test, success;
     as_truncwd(ScratchDoubleReg, src);
     as_mfc1(dest, ScratchDoubleReg);
 
     ma_b(dest, Imm32(INT32_MAX), fail, Assembler::Equal);
     ma_b(dest, Imm32(INT32_MIN), fail, Assembler::Equal);
 }
 
+void
+MacroAssembler::branchTruncateDoubleToInt32(FloatRegister src, Register dest, Label* fail)
+{
+    convertDoubleToInt32(src, dest, fail);
+}
+
 template <typename T>
 void
 MacroAssembler::branchAdd32(Condition cond, T src, Register dest, Label* overflow)
 {
     switch (cond) {
       case Overflow:
         ma_addTestOverflow(dest, dest, src, overflow);
         break;
--- a/js/src/jit/mips32/MacroAssembler-mips32.cpp
+++ b/js/src/jit/mips32/MacroAssembler-mips32.cpp
@@ -1107,17 +1107,17 @@ MacroAssembler::clampDoubleToUint8(Float
     bind(&positive);
 
     // Add 0.5 and truncate.
     loadConstantDouble(0.5, ScratchDoubleReg);
     addDouble(ScratchDoubleReg, input);
 
     Label outOfRange;
 
-    branchTruncateDouble(input, output, &outOfRange);
+    branchTruncateDoubleMaybeModUint32(input, output, &outOfRange);
     asMasm().branch32(Assembler::Above, output, Imm32(255), &outOfRange);
     {
         // Check if we had a tie.
         convertInt32ToDouble(output, ScratchDoubleReg);
         branchDouble(DoubleNotEqual, input, ScratchDoubleReg, &done);
 
         // It was a tie. Mask out the ones bit to get an even value.
         // See also js_TypedArray_uint8_clamp_double.
--- a/js/src/jit/mips64/MacroAssembler-mips64.cpp
+++ b/js/src/jit/mips64/MacroAssembler-mips64.cpp
@@ -1254,17 +1254,17 @@ MacroAssembler::clampDoubleToUint8(Float
     bind(&positive);
 
     // Add 0.5 and truncate.
     loadConstantDouble(0.5, ScratchDoubleReg);
     addDouble(ScratchDoubleReg, input);
 
     Label outOfRange;
 
-    branchTruncateDouble(input, output, &outOfRange);
+    branchTruncateDoubleMaybeModUint32(input, output, &outOfRange);
     asMasm().branch32(Assembler::Above, output, Imm32(255), &outOfRange);
     {
         // Check if we had a tie.
         convertInt32ToDouble(output, ScratchDoubleReg);
         branchDouble(DoubleNotEqual, input, ScratchDoubleReg, &done);
 
         // It was a tie. Mask out the ones bit to get an even value.
         // See also js_TypedArray_uint8_clamp_double.
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -1445,27 +1445,27 @@ CodeGeneratorShared::oolTruncateDouble(F
     return ool;
 }
 
 void
 CodeGeneratorShared::emitTruncateDouble(FloatRegister src, Register dest, MInstruction* mir)
 {
     OutOfLineCode* ool = oolTruncateDouble(src, dest, mir);
 
-    masm.branchTruncateDouble(src, dest, ool->entry());
+    masm.branchTruncateDoubleMaybeModUint32(src, dest, ool->entry());
     masm.bind(ool->rejoin());
 }
 
 void
 CodeGeneratorShared::emitTruncateFloat32(FloatRegister src, Register dest, MInstruction* mir)
 {
     OutOfLineTruncateSlow* ool = new(alloc()) OutOfLineTruncateSlow(src, dest, true);
     addOutOfLineCode(ool, mir);
 
-    masm.branchTruncateFloat32(src, dest, ool->entry());
+    masm.branchTruncateFloat32MaybeModUint32(src, dest, ool->entry());
     masm.bind(ool->rejoin());
 }
 
 void
 CodeGeneratorShared::visitOutOfLineTruncateSlow(OutOfLineTruncateSlow* ool)
 {
     FloatRegister src = ool->src();
     Register dest = ool->dest();
--- a/js/src/jit/x64/MacroAssembler-x64-inl.h
+++ b/js/src/jit/x64/MacroAssembler-x64-inl.h
@@ -642,42 +642,66 @@ MacroAssembler::branchPrivatePtr(Conditi
     if (rhs != scratch)
         movePtr(rhs, scratch);
     // Instead of unboxing lhs, box rhs and do direct comparison with lhs.
     rshiftPtr(Imm32(1), scratch);
     branchPtr(cond, lhs, scratch, label);
 }
 
 void
-MacroAssembler::branchTruncateFloat32(FloatRegister src, Register dest, Label* fail)
+MacroAssembler::branchTruncateFloat32ToPtr(FloatRegister src, Register dest, Label* fail)
 {
     vcvttss2sq(src, dest);
 
     // Same trick as for Doubles
     cmpPtr(dest, Imm32(1));
     j(Assembler::Overflow, fail);
+}
 
+void
+MacroAssembler::branchTruncateFloat32MaybeModUint32(FloatRegister src, Register dest, Label* fail)
+{
+    branchTruncateFloat32ToPtr(src, dest, fail);
     movl(dest, dest); // Zero upper 32-bits.
 }
 
 void
-MacroAssembler::branchTruncateDouble(FloatRegister src, Register dest, Label* fail)
+MacroAssembler::branchTruncateFloat32ToInt32(FloatRegister src, Register dest, Label* fail)
+{
+    branchTruncateFloat32ToPtr(src, dest, fail);
+    branch32(Assembler::Above, dest, Imm32(0xffffffff), fail);
+}
+
+void
+MacroAssembler::branchTruncateDoubleToPtr(FloatRegister src, Register dest, Label* fail)
 {
     vcvttsd2sq(src, dest);
 
     // vcvttsd2sq returns 0x8000000000000000 on failure. Test for it by
     // subtracting 1 and testing overflow (this avoids the need to
     // materialize that value in a register).
     cmpPtr(dest, Imm32(1));
     j(Assembler::Overflow, fail);
+}
 
+void
+MacroAssembler::branchTruncateDoubleMaybeModUint32(FloatRegister src, Register dest, Label* fail)
+{
+    branchTruncateDoubleToPtr(src, dest, fail);
     movl(dest, dest); // Zero upper 32-bits.
 }
 
 void
+MacroAssembler::branchTruncateDoubleToInt32(FloatRegister src, Register dest, Label* fail)
+{
+    branchTruncateDoubleToPtr(src, dest, fail);
+    branch32(Assembler::Above, dest, Imm32(0xffffffff), fail);
+}
+
+void
 MacroAssembler::branchTest32(Condition cond, const AbsoluteAddress& lhs, Imm32 rhs, Label* label)
 {
     if (X86Encoding::IsAddressImmediate(lhs.addr)) {
         test32(Operand(lhs), rhs);
     } else {
         ScratchRegisterScope scratch(*this);
         mov(ImmPtr(lhs.addr), scratch);
         test32(Operand(scratch, 0), rhs);
@@ -702,16 +726,74 @@ MacroAssembler::branchTestBooleanTruthy(
 
 void
 MacroAssembler::branchTestMagic(Condition cond, const Address& valaddr, JSWhyMagic why, Label* label)
 {
     uint64_t magic = MagicValue(why).asRawBits();
     cmpPtr(valaddr, ImmWord(magic));
     j(cond, label);
 }
+// ========================================================================
+// Truncate floating point.
+
+void
+MacroAssembler::truncateFloat32ToUInt64(Address src, Address dest, Register temp,
+                                        FloatRegister floatTemp)
+{
+    Label done;
+
+    loadFloat32(src, floatTemp);
+
+    truncateFloat32ToInt64(src, dest, temp);
+
+    // For unsigned conversion the case of [INT64, UINT64] needs to get handle seperately.
+    loadPtr(dest, temp);
+    branch32(Assembler::Condition::NotSigned, temp, Imm32(0), &done);
+
+    // Move the value inside INT64 range.
+    storeFloat32(floatTemp, dest);
+    loadConstantFloat32(double(int64_t(0x8000000000000000)), floatTemp);
+    vaddss(Operand(dest), floatTemp, floatTemp);
+    storeFloat32(floatTemp, dest);
+    truncateFloat32ToInt64(dest, dest, temp);
+
+    loadPtr(dest, temp);
+    or64(Imm64(0x8000000000000000), Register64(temp));
+    storePtr(temp, dest);
+
+    bind(&done);
+}
+
+void
+MacroAssembler::truncateDoubleToUInt64(Address src, Address dest, Register temp,
+                                       FloatRegister floatTemp)
+{
+    Label done;
+
+    loadDouble(src, floatTemp);
+
+    truncateDoubleToInt64(src, dest, temp);
+
+    // For unsigned conversion the case of [INT64, UINT64] needs to get handle seperately.
+    loadPtr(dest, temp);
+    branch32(Assembler::Condition::NotSigned, temp, Imm32(0), &done);
+
+    // Move the value inside INT64 range.
+    storeDouble(floatTemp, dest);
+    loadConstantDouble(double(int64_t(0x8000000000000000)), floatTemp);
+    vaddsd(Operand(dest), floatTemp, floatTemp);
+    storeDouble(floatTemp, dest);
+    truncateDoubleToInt64(dest, dest, temp);
+
+    loadPtr(dest, temp);
+    or64(Imm64(0x8000000000000000), Register64(temp));
+    storePtr(temp, dest);
+
+    bind(&done);
+}
 
 //}}} check_macroassembler_style
 // ===============================================================
 
 void
 MacroAssemblerX64::incrementInt32Value(const Address& addr)
 {
     asMasm().addPtr(Imm32(1), addr);
--- a/js/src/jit/x86-shared/Assembler-x86-shared.h
+++ b/js/src/jit/x86-shared/Assembler-x86-shared.h
@@ -3511,25 +3511,70 @@ class AssemblerX86Shared : public Assemb
         switch (dest.kind()) {
           case Operand::MEM_REG_DISP:
             masm.fisttp_m(dest.disp(), dest.base());
             break;
           default:
             MOZ_CRASH("unexpected operand kind");
         }
     }
+    void fistp(const Operand& dest) {
+        switch (dest.kind()) {
+          case Operand::MEM_REG_DISP:
+            masm.fistp_m(dest.disp(), dest.base());
+            break;
+          default:
+            MOZ_CRASH("unexpected operand kind");
+        }
+    }
+    void fnstcw(const Operand& dest) {
+        switch (dest.kind()) {
+          case Operand::MEM_REG_DISP:
+            masm.fnstcw_m(dest.disp(), dest.base());
+            break;
+          default:
+            MOZ_CRASH("unexpected operand kind");
+        }
+    }
+    void fldcw(const Operand& dest) {
+        switch (dest.kind()) {
+          case Operand::MEM_REG_DISP:
+            masm.fldcw_m(dest.disp(), dest.base());
+            break;
+          default:
+            MOZ_CRASH("unexpected operand kind");
+        }
+    }
+    void fnstsw(const Operand& dest) {
+        switch (dest.kind()) {
+          case Operand::MEM_REG_DISP:
+            masm.fnstsw_m(dest.disp(), dest.base());
+            break;
+          default:
+            MOZ_CRASH("unexpected operand kind");
+        }
+    }
     void fld(const Operand& dest) {
         switch (dest.kind()) {
           case Operand::MEM_REG_DISP:
             masm.fld_m(dest.disp(), dest.base());
             break;
           default:
             MOZ_CRASH("unexpected operand kind");
         }
     }
+    void fld32(const Operand& dest) {
+        switch (dest.kind()) {
+          case Operand::MEM_REG_DISP:
+            masm.fld32_m(dest.disp(), dest.base());
+            break;
+          default:
+            MOZ_CRASH("unexpected operand kind");
+        }
+    }
     void fstp(const Operand& src) {
         switch (src.kind()) {
           case Operand::MEM_REG_DISP:
             masm.fstp_m(src.disp(), src.base());
             break;
           default:
             MOZ_CRASH("unexpected operand kind");
         }
--- a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h
+++ b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h
@@ -1010,26 +1010,46 @@ public:
         spew("fld        " MEM_ob, ADDR_ob(offset, base));
         m_formatter.oneByteOp(OP_FPU6_F32, offset, base, FPU6_OP_FLD);
     }
     void fisttp_m(int32_t offset, RegisterID base)
     {
         spew("fisttp     " MEM_ob, ADDR_ob(offset, base));
         m_formatter.oneByteOp(OP_FPU6, offset, base, FPU6_OP_FISTTP);
     }
+    void fistp_m(int32_t offset, RegisterID base)
+    {
+        spew("fistp      " MEM_ob, ADDR_ob(offset, base));
+        m_formatter.oneByteOp(OP_FILD, offset, base, FPU6_OP_FISTP);
+    }
     void fstp_m(int32_t offset, RegisterID base)
     {
         spew("fstp       " MEM_ob, ADDR_ob(offset, base));
         m_formatter.oneByteOp(OP_FPU6, offset, base, FPU6_OP_FSTP);
     }
     void fstp32_m(int32_t offset, RegisterID base)
     {
-        spew("fstp32       " MEM_ob, ADDR_ob(offset, base));
+        spew("fstp32     " MEM_ob, ADDR_ob(offset, base));
         m_formatter.oneByteOp(OP_FPU6_F32, offset, base, FPU6_OP_FSTP);
     }
+    void fnstcw_m(int32_t offset, RegisterID base)
+    {
+        spew("fnstcw     " MEM_ob, ADDR_ob(offset, base));
+        m_formatter.oneByteOp(OP_FPU6_F32, offset, base, FPU6_OP_FISTP);
+    }
+    void fldcw_m(int32_t offset, RegisterID base)
+    {
+        spew("fldcw      " MEM_ob, ADDR_ob(offset, base));
+        m_formatter.oneByteOp(OP_FPU6_F32, offset, base, FPU6_OP_FLDCW);
+    }
+    void fnstsw_m(int32_t offset, RegisterID base)
+    {
+        spew("fnstsw     " MEM_ob, ADDR_ob(offset, base));
+        m_formatter.oneByteOp(OP_FPU6, offset, base, FPU6_OP_FISTP);
+    }
 
     void negl_r(RegisterID dst)
     {
         spew("negl       %s", GPReg32Name(dst));
         m_formatter.oneByteOp(OP_GROUP3_Ev, dst, GROUP3_OP_NEG);
     }
 
     void negl_m(int32_t offset, RegisterID base)
--- a/js/src/jit/x86-shared/Encoding-x86-shared.h
+++ b/js/src/jit/x86-shared/Encoding-x86-shared.h
@@ -136,16 +136,17 @@ enum OneByteOpcodeID {
     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_F32                     = 0xD9,
+    OP_FILD                         = 0xDF,
     OP_CALL_rel32                   = 0xE8,
     OP_JMP_rel32                    = 0xE9,
     OP_JMP_rel8                     = 0xEB,
     PRE_LOCK                        = 0xF0,
     PRE_SSE_F2                      = 0xF2,
     PRE_SSE_F3                      = 0xF3,
     OP_HLT                          = 0xF4,
     OP_GROUP3_EbIb                  = 0xF6,
@@ -374,16 +375,18 @@ enum GroupOpcodeID {
     GROUP5_OP_DEC   = 1,
     GROUP5_OP_CALLN = 2,
     GROUP5_OP_JMPN  = 4,
     GROUP5_OP_PUSH  = 6,
 
     FPU6_OP_FLD     = 0,
     FPU6_OP_FISTTP  = 1,
     FPU6_OP_FSTP    = 3,
+    FPU6_OP_FLDCW   = 5,
+    FPU6_OP_FISTP   = 7,
 
     GROUP11_MOV = 0
 };
 
 static const RegisterID noBase = rbp;
 static const RegisterID hasSib = rsp;
 static const RegisterID noIndex = rsp;
 #ifdef JS_CODEGEN_X64
--- a/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h
+++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h
@@ -1152,16 +1152,87 @@ MacroAssembler::storeFloat32x3(FloatRegi
     BaseIndex destZ(dest);
     destZ.offset += 2 * sizeof(int32_t);
     storeDouble(src, dest);
     ScratchSimd128Scope scratch(*this);
     vmovhlps(src, scratch, scratch);
     storeFloat32(scratch, destZ);
 }
 
+
+// ========================================================================
+// Truncate floating point.
+
+void
+MacroAssembler::truncateFloat32ToInt64(Address src, Address dest, Register temp)
+{
+    if (Assembler::HasSSE3()) {
+        fld32(Operand(src));
+        fisttp(Operand(dest));
+        return;
+    }
+
+    if (src.base == esp)
+        src.offset += 2 * sizeof(int32_t);
+    if (dest.base == esp)
+        dest.offset += 2 * sizeof(int32_t);
+
+    reserveStack(2*sizeof(int32_t));
+
+    // Set conversion to truncation.
+    fnstcw(Operand(esp, 0));
+    load32(Operand(esp, 0), temp);
+    andl(Imm32(~0xFF00), temp);
+    orl(Imm32(0xCFF), temp);
+    store32(temp, Address(esp, 1*sizeof(int32_t)));
+    fldcw(Operand(esp, 1*sizeof(int32_t)));
+
+    // Load double on fp stack, convert and load regular stack.
+    fld32(Operand(src));
+    fistp(Operand(dest));
+
+    // Reset the conversion flag.
+    fldcw(Operand(esp, 0));
+
+    freeStack(2*sizeof(int32_t));
+}
+void
+MacroAssembler::truncateDoubleToInt64(Address src, Address dest, Register temp)
+{
+    if (Assembler::HasSSE3()) {
+        fld(Operand(src));
+        fisttp(Operand(dest));
+        return;
+    }
+
+    if (src.base == esp)
+        src.offset += 2*sizeof(int32_t);
+    if (dest.base == esp)
+        dest.offset += 2*sizeof(int32_t);
+
+    reserveStack(2*sizeof(int32_t));
+
+    // Set conversion to truncation.
+    fnstcw(Operand(esp, 0));
+    load32(Operand(esp, 0), temp);
+    andl(Imm32(~0xFF00), temp);
+    orl(Imm32(0xCFF), temp);
+    store32(temp, Address(esp, 1*sizeof(int32_t)));
+    fldcw(Operand(esp, 1*sizeof(int32_t)));
+
+    // Load double on fp stack, convert and load regular stack.
+    fld(Operand(src));
+    fistp(Operand(dest));
+
+    // Reset the conversion flag.
+    fldcw(Operand(esp, 0));
+
+    freeStack(2*sizeof(int32_t));
+}
+
 //}}} check_macroassembler_style
 // ===============================================================
 
 void
 MacroAssemblerX86Shared::clampIntToUint8(Register reg)
 {
     Label inRange;
     asMasm().branchTest32(Assembler::Zero, reg, Imm32(0xffffff00), &inRange);
--- a/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp
+++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp
@@ -452,16 +452,32 @@ MacroAssemblerX86Shared::outOfLineWasmTr
             } else {
                 // We've used vcvtss2sq. Same comment applies.
                 MOZ_ASSERT(fromType == MIRType::Float32);
                 asMasm().loadConstantFloat32(float(int64_t(INT64_MIN)), ScratchFloat32Reg);
                 asMasm().branchFloat(Assembler::DoubleNotEqual, input, ScratchFloat32Reg, &fail);
             }
         }
         jump(rejoin);
+    } else {
+        if (toType == MIRType::Int64) {
+            if (fromType == MIRType::Double) {
+                asMasm().loadConstantDouble(double(-0.0), ScratchDoubleReg);
+                asMasm().branchDouble(Assembler::DoubleGreaterThan, input, ScratchDoubleReg, &fail);
+                asMasm().loadConstantDouble(double(-1.0), ScratchDoubleReg);
+                asMasm().branchDouble(Assembler::DoubleLessThanOrEqual, input, ScratchDoubleReg, &fail);
+            } else {
+                MOZ_ASSERT(fromType == MIRType::Float32);
+                asMasm().loadConstantFloat32(double(-0.0), ScratchDoubleReg);
+                asMasm().branchFloat(Assembler::DoubleGreaterThan, input, ScratchFloat32Reg, &fail);
+                asMasm().loadConstantFloat32(double(-1.0), ScratchDoubleReg);
+                asMasm().branchFloat(Assembler::DoubleLessThanOrEqual, input, ScratchFloat32Reg, &fail);
+            }
+            jump(rejoin);
+        }
     }
 
     // Handle errors.
     bind(&fail);
     jump(wasm::JumpTarget::IntegerOverflow);
 
     bind(&inputIsNaN);
     jump(wasm::JumpTarget::InvalidConversionToInteger);
--- a/js/src/jit/x86/Assembler-x86.h
+++ b/js/src/jit/x86/Assembler-x86.h
@@ -331,26 +331,16 @@ class Assembler : public AssemblerX86Sha
     }
     void xchg(Register src, Register dest) {
         xchgl(src, dest);
     }
     void lea(const Operand& src, 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_CRASH("unexpected operand kind");
-        }
-    }
-
     void fstp32(const Operand& src) {
         switch (src.kind()) {
           case Operand::MEM_REG_DISP:
             masm.fstp32_m(src.disp(), src.base());
             break;
           default:
             MOZ_CRASH("unexpected operand kind");
         }
--- a/js/src/jit/x86/CodeGenerator-x86.cpp
+++ b/js/src/jit/x86/CodeGenerator-x86.cpp
@@ -945,61 +945,53 @@ void
 CodeGeneratorX86::visitTruncateDToInt32(LTruncateDToInt32* ins)
 {
     FloatRegister input = ToFloatRegister(ins->input());
     Register output = ToRegister(ins->output());
 
     OutOfLineTruncate* ool = new(alloc()) OutOfLineTruncate(ins);
     addOutOfLineCode(ool, ins->mir());
 
-    masm.branchTruncateDouble(input, output, ool->entry());
+    masm.branchTruncateDoubleMaybeModUint32(input, output, ool->entry());
     masm.bind(ool->rejoin());
 }
 
 void
 CodeGeneratorX86::visitTruncateFToInt32(LTruncateFToInt32* ins)
 {
     FloatRegister input = ToFloatRegister(ins->input());
     Register output = ToRegister(ins->output());
 
     OutOfLineTruncateFloat32* ool = new(alloc()) OutOfLineTruncateFloat32(ins);
     addOutOfLineCode(ool, ins->mir());
 
-    masm.branchTruncateFloat32(input, output, ool->entry());
+    masm.branchTruncateFloat32MaybeModUint32(input, output, ool->entry());
     masm.bind(ool->rejoin());
 }
 
 void
 CodeGeneratorX86::visitOutOfLineTruncate(OutOfLineTruncate* ool)
 {
     LTruncateDToInt32* ins = ool->ins();
     FloatRegister input = ToFloatRegister(ins->input());
     Register output = ToRegister(ins->output());
 
     Label fail;
 
     if (Assembler::HasSSE3()) {
+        Label failPopDouble;
         // Push double.
         masm.subl(Imm32(sizeof(double)), esp);
         masm.storeDouble(input, Operand(esp, 0));
 
-        static const uint32_t EXPONENT_MASK = 0x7ff00000;
-        static const uint32_t EXPONENT_SHIFT = FloatingPoint<double>::kExponentShift - 32;
-        static const uint32_t TOO_BIG_EXPONENT = (FloatingPoint<double>::kExponentBias + 63)
-                                                 << EXPONENT_SHIFT;
-
         // Check exponent to avoid fp exceptions.
-        Label failPopDouble;
-        masm.load32(Address(esp, 4), output);
-        masm.and32(Imm32(EXPONENT_MASK), output);
-        masm.branch32(Assembler::GreaterThanOrEqual, output, Imm32(TOO_BIG_EXPONENT), &failPopDouble);
+        masm.branchDoubleNotInInt64Range(Address(esp, 0), output, &failPopDouble);
 
         // Load double, perform 64-bit truncation.
-        masm.fld(Operand(esp, 0));
-        masm.fisttp(Operand(esp, 0));
+        masm.truncateDoubleToInt64(Address(esp, 0), Address(esp, 0), output);
 
         // Load low word, pop double and jump back.
         masm.load32(Address(esp, 0), output);
         masm.addl(Imm32(sizeof(double)), esp);
         masm.jump(ool->rejoin());
 
         masm.bind(&failPopDouble);
         masm.addl(Imm32(sizeof(double)), esp);
@@ -1060,35 +1052,27 @@ CodeGeneratorX86::visitOutOfLineTruncate
 {
     LTruncateFToInt32* ins = ool->ins();
     FloatRegister input = ToFloatRegister(ins->input());
     Register output = ToRegister(ins->output());
 
     Label fail;
 
     if (Assembler::HasSSE3()) {
+        Label failPopFloat;
+
         // Push float32, but subtracts 64 bits so that the value popped by fisttp fits
         masm.subl(Imm32(sizeof(uint64_t)), esp);
         masm.storeFloat32(input, Operand(esp, 0));
 
-        static const uint32_t EXPONENT_MASK = FloatingPoint<float>::kExponentBits;
-        static const uint32_t EXPONENT_SHIFT = FloatingPoint<float>::kExponentShift;
-        // Integers are still 64 bits long, so we can still test for an exponent > 63.
-        static const uint32_t TOO_BIG_EXPONENT = (FloatingPoint<float>::kExponentBias + 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);
+        masm.branchDoubleNotInInt64Range(Address(esp, 0), output, &failPopFloat);
 
         // Load float, perform 32-bit truncation.
-        masm.fld32(Operand(esp, 0));
-        masm.fisttp(Operand(esp, 0));
+        masm.truncateFloat32ToInt64(Address(esp, 0), Address(esp, 0), output);
 
         // Load low word, pop 64bits and jump back.
         masm.load32(Address(esp, 0), output);
         masm.addl(Imm32(sizeof(uint64_t)), esp);
         masm.jump(ool->rejoin());
 
         masm.bind(&failPopFloat);
         masm.addl(Imm32(sizeof(uint64_t)), esp);
@@ -1506,8 +1490,70 @@ CodeGeneratorX86::visitNotI64(LNotI64* l
     } else {
         masm.movl(input.high, output);
         masm.orl(input.low, output);
     }
 
     masm.cmpl(Imm32(0), output);
     masm.emitSet(Assembler::Equal, output);
 }
+
+void
+CodeGeneratorX86::visitWasmTruncateToInt64(LWasmTruncateToInt64* lir)
+{
+    FloatRegister input = ToFloatRegister(lir->input());
+    Register64 output = ToOutRegister64(lir);
+
+    MWasmTruncateToInt64* mir = lir->mir();
+    Register temp = ToRegister(lir->temp1());
+    FloatRegister floatTemp = ToFloatRegister(lir->temp2());
+
+    Label fail, convert;
+
+    MOZ_ASSERT (mir->input()->type() == MIRType::Double || mir->input()->type() == MIRType::Float32);
+
+    auto* ool = new(alloc()) OutOfLineWasmTruncateCheck(mir, input);
+    addOutOfLineCode(ool, mir);
+
+    masm.reserveStack(2*sizeof(int32_t));
+    masm.storeDouble(input, Operand(esp, 0));
+
+    // Make sure input fits in (u)int64
+    if (mir->input()->type() == MIRType::Float32) {
+        if (mir->isUnsigned())
+            masm.branchFloat32NotInUInt64Range(Address(esp, 0), temp, &fail);
+        else
+            masm.branchFloat32NotInInt64Range(Address(esp, 0), temp, &fail);
+    } else {
+        if (mir->isUnsigned())
+            masm.branchDoubleNotInUInt64Range(Address(esp, 0), temp, &fail);
+        else
+            masm.branchDoubleNotInInt64Range(Address(esp, 0), temp, &fail);
+    }
+    masm.jump(&convert);
+
+    // Handle failure in ool
+    masm.bind(&fail);
+    masm.freeStack(2*sizeof(int32_t));
+    masm.jump(ool->entry());
+    masm.bind(ool->rejoin());
+    masm.reserveStack(2*sizeof(int32_t));
+    masm.storeDouble(input, Operand(esp, 0));
+
+    // Convert the double/float to int64
+    masm.bind(&convert);
+    if (mir->input()->type() == MIRType::Float32) {
+        if (mir->isUnsigned())
+            masm.truncateFloat32ToUInt64(Address(esp, 0), Address(esp, 0), temp, floatTemp);
+        else
+            masm.truncateFloat32ToInt64(Address(esp, 0), Address(esp, 0), temp);
+    } else {
+        if (mir->isUnsigned())
+            masm.truncateDoubleToUInt64(Address(esp, 0), Address(esp, 0), temp, floatTemp);
+        else
+            masm.truncateDoubleToInt64(Address(esp, 0), Address(esp, 0), temp);
+    }
+
+    // Load value into float register.
+    masm.load64(Address(esp, 0), output);
+
+    masm.freeStack(2*sizeof(int32_t));
+}
--- a/js/src/jit/x86/CodeGenerator-x86.h
+++ b/js/src/jit/x86/CodeGenerator-x86.h
@@ -80,16 +80,17 @@ class CodeGeneratorX86 : public CodeGene
     void visitAsmSelectI64(LAsmSelectI64* lir);
     void visitAsmReinterpretFromI64(LAsmReinterpretFromI64* lir);
     void visitAsmReinterpretToI64(LAsmReinterpretToI64* lir);
     void visitExtendInt32ToInt64(LExtendInt32ToInt64* lir);
     void visitWrapInt64ToInt32(LWrapInt64ToInt32* lir);
     void visitClzI64(LClzI64* lir);
     void visitCtzI64(LCtzI64* lir);
     void visitNotI64(LNotI64* lir);
+    void visitWasmTruncateToInt64(LWasmTruncateToInt64* lir);
 
   private:
     void asmJSAtomicComputeAddress(Register addrTemp, Register ptrReg,
                                    const MWasmMemoryAccess* access);
 };
 
 typedef CodeGeneratorX86 CodeGeneratorSpecific;
 
--- a/js/src/jit/x86/LIR-x86.h
+++ b/js/src/jit/x86/LIR-x86.h
@@ -164,12 +164,42 @@ class LUDivOrModI64 : public LCallInstru
     }
     bool canBeNegativeOverflow() const {
         if (mir_->isMod())
             return mir_->toMod()->canBeNegativeDividend();
         return mir_->toDiv()->canBeNegativeOverflow();
     }
 };
 
+class LWasmTruncateToInt64 : public LInstructionHelper<INT64_PIECES, 1, 3>
+{
+  public:
+    LIR_HEADER(WasmTruncateToInt64);
+
+    LWasmTruncateToInt64(const LAllocation& in, const LDefinition& temp1, const LDefinition& temp2,
+                         const LDefinition& temp3)
+    {
+        setOperand(0, in);
+        setTemp(0, temp1);
+        setTemp(1, temp2);
+        setTemp(2, temp3);
+    }
+
+    MWasmTruncateToInt64* mir() const {
+        return mir_->toWasmTruncateToInt64();
+    }
+
+    const LDefinition* temp1() {
+        return getTemp(0);
+    }
+    const LDefinition* temp2() {
+        return getTemp(1);
+    }
+    const LDefinition* temp3() {
+        MOZ_ASSERT(mir()->isUnsigned());
+        return getTemp(2);
+    }
+};
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_x86_LIR_x86_h */
--- a/js/src/jit/x86/LOpcodes-x86.h
+++ b/js/src/jit/x86/LOpcodes-x86.h
@@ -12,11 +12,12 @@
 #define LIR_CPU_OPCODE_LIST(_)  \
     _(BoxFloatingPoint)         \
     _(DivOrModConstantI)        \
     _(SimdValueInt32x4)         \
     _(SimdValueFloat32x4)       \
     _(UDivOrMod)                \
     _(UDivOrModConstant)        \
     _(UDivOrModI64)             \
-    _(DivOrModI64)
+    _(DivOrModI64)              \
+    _(WasmTruncateToInt64)
 
 #endif /* jit_x86_LOpcodes_x86_h */
--- a/js/src/jit/x86/Lowering-x86.cpp
+++ b/js/src/jit/x86/Lowering-x86.cpp
@@ -584,17 +584,23 @@ LIRGeneratorX86::visitRandom(MRandom* in
                                         temp(),
                                         temp());
     defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
 }
 
 void
 LIRGeneratorX86::visitWasmTruncateToInt64(MWasmTruncateToInt64* ins)
 {
-    MOZ_CRASH("NY");
+    MDefinition* opd = ins->input();
+    MOZ_ASSERT(opd->type() == MIRType::Double || opd->type() == MIRType::Float32);
+
+    LDefinition temp1 = temp();
+    LDefinition temp2 = tempDouble();
+    LDefinition maybeTemp = ins->isUnsigned() ? tempDouble() : LDefinition::BogusTemp();
+    defineInt64(new(alloc()) LWasmTruncateToInt64(useRegister(opd), temp1, temp2, maybeTemp), ins);
 }
 
 void
 LIRGeneratorX86::visitInt64ToFloatingPoint(MInt64ToFloatingPoint* ins)
 {
     MOZ_CRASH("NY");
 }
 
--- a/js/src/jit/x86/MacroAssembler-x86-inl.h
+++ b/js/src/jit/x86/MacroAssembler-x86-inl.h
@@ -759,29 +759,55 @@ MacroAssembler::branchPtr(Condition cond
 
 void
 MacroAssembler::branchPrivatePtr(Condition cond, const Address& lhs, Register rhs, Label* label)
 {
     branchPtr(cond, lhs, rhs, label);
 }
 
 void
-MacroAssembler::branchTruncateFloat32(FloatRegister src, Register dest, Label* fail)
+MacroAssembler::branchTruncateFloat32ToPtr(FloatRegister src, Register dest, Label* fail)
+{
+    branchTruncateFloat32ToInt32(src, dest, fail);
+}
+
+void
+MacroAssembler::branchTruncateFloat32MaybeModUint32(FloatRegister src, Register dest, Label* fail)
+{
+    branchTruncateFloat32ToInt32(src, dest, fail);
+}
+
+void
+MacroAssembler::branchTruncateFloat32ToInt32(FloatRegister src, Register dest, Label* fail)
 {
     vcvttss2si(src, dest);
 
     // vcvttss2si returns 0x80000000 on failure. Test for it by
     // subtracting 1 and testing overflow (this permits the use of a
     // smaller immediate field).
     cmp32(dest, Imm32(1));
     j(Assembler::Overflow, fail);
 }
 
 void
-MacroAssembler::branchTruncateDouble(FloatRegister src, Register dest, Label* fail)
+MacroAssembler::branchTruncateDoubleToPtr(FloatRegister src, Register dest, Label* fail)
+{
+    branchTruncateDoubleToInt32(src, dest, fail);
+}
+
+void
+MacroAssembler::branchTruncateDoubleMaybeModUint32(FloatRegister src, Register dest, Label* fail)
+{
+    // TODO: X64 supports supports integers up till 64bits. Here we only support 32bits,
+    // before failing. Implementing this for x86 might give a x86 kraken win.
+    branchTruncateDoubleToInt32(src, dest, fail);
+}
+
+void
+MacroAssembler::branchTruncateDoubleToInt32(FloatRegister src, Register dest, Label* fail)
 {
     vcvttsd2si(src, dest);
 
     // vcvttsd2si returns 0x80000000 on failure. Test for it by
     // subtracting 1 and testing overflow (this permits the use of a
     // smaller immediate field).
     cmp32(dest, Imm32(1));
     j(Assembler::Overflow, fail);
@@ -819,16 +845,75 @@ MacroAssembler::branchTestBooleanTruthy(
 
 void
 MacroAssembler::branchTestMagic(Condition cond, const Address& valaddr, JSWhyMagic why, Label* label)
 {
     branchTestMagic(cond, valaddr, label);
     branch32(cond, ToPayload(valaddr), Imm32(why), label);
 }
 
+// ========================================================================
+// Truncate floating point.
+
+void
+MacroAssembler::truncateFloat32ToUInt64(Address src, Address dest, Register temp,
+                                        FloatRegister floatTemp)
+{
+    Label done;
+
+    loadFloat32(src, floatTemp);
+
+    truncateFloat32ToInt64(src, dest, temp);
+
+    // For unsigned conversion the case of [INT64, UINT64] needs to get handle seperately.
+    load32(Address(dest.base, dest.offset + INT64HIGH_OFFSET), temp);
+    branch32(Assembler::Condition::NotSigned, temp, Imm32(0), &done);
+
+    // Move the value inside INT64 range.
+    storeFloat32(floatTemp, dest);
+    loadConstantFloat32(double(int64_t(0x8000000000000000)), floatTemp);
+    vaddss(Operand(dest), floatTemp, floatTemp);
+    storeFloat32(floatTemp, dest);
+    truncateFloat32ToInt64(dest, dest, temp);
+
+    load32(Address(dest.base, dest.offset + INT64HIGH_OFFSET), temp);
+    orl(Imm32(0x80000000), temp);
+    store32(temp, Address(dest.base, dest.offset + INT64HIGH_OFFSET));
+
+    bind(&done);
+}
+
+void
+MacroAssembler::truncateDoubleToUInt64(Address src, Address dest, Register temp,
+                                       FloatRegister floatTemp)
+{
+    Label done;
+
+    loadDouble(src, floatTemp);
+
+    truncateDoubleToInt64(src, dest, temp);
+
+    // For unsigned conversion the case of [INT64, UINT64] needs to get handle seperately.
+    load32(Address(dest.base, dest.offset + INT64HIGH_OFFSET), temp);
+    branch32(Assembler::Condition::NotSigned, temp, Imm32(0), &done);
+
+    // Move the value inside INT64 range.
+    storeDouble(floatTemp, dest);
+    loadConstantDouble(double(int64_t(0x8000000000000000)), floatTemp);
+    vaddsd(Operand(dest), floatTemp, floatTemp);
+    storeDouble(floatTemp, dest);
+    truncateDoubleToInt64(dest, dest, temp);
+
+    load32(Address(dest.base, dest.offset + INT64HIGH_OFFSET), temp);
+    orl(Imm32(0x80000000), temp);
+    store32(temp, Address(dest.base, dest.offset + INT64HIGH_OFFSET));
+
+    bind(&done);
+}
+
 //}}} check_macroassembler_style
 // ===============================================================
 
 // Note: this function clobbers the source register.
 void
 MacroAssemblerX86::convertUInt32ToDouble(Register src, FloatRegister dest)
 {
     // src is [0, 2^32-1]