Bug 1252432 part 3 - Implement wasm i64.trunc_s and i64.trunc_u. r=sunfish
authorJan de Mooij <jdemooij@mozilla.com>
Fri, 04 Mar 2016 13:57:44 +0100
changeset 286787 36a77fdd0533553090e6ff53809d1b31dd23bdac
parent 286786 3231cc3fd19e28e9718716a8da85041c262a3be8
child 286788 77192f60c4a5992651c9a9c2550adf6dd3ece42f
push id30056
push userryanvm@gmail.com
push dateSun, 06 Mar 2016 00:19:57 +0000
treeherdermozilla-central@fcd55efa0672 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssunfish
bugs1252432
milestone47.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 1252432 part 3 - Implement wasm i64.trunc_s and i64.trunc_u. r=sunfish
js/src/asmjs/Wasm.cpp
js/src/asmjs/WasmIonCompile.cpp
js/src/jit-test/tests/wasm/basic-conversion.js
js/src/jit/MIR.h
js/src/jit/MOpcodes.h
js/src/jit/arm/Lowering-arm.cpp
js/src/jit/arm/Lowering-arm.h
js/src/jit/arm64/Lowering-arm64.cpp
js/src/jit/arm64/Lowering-arm64.h
js/src/jit/mips-shared/Lowering-mips-shared.cpp
js/src/jit/mips-shared/Lowering-mips-shared.h
js/src/jit/none/Lowering-none.h
js/src/jit/x64/CodeGenerator-x64.cpp
js/src/jit/x64/CodeGenerator-x64.h
js/src/jit/x64/LIR-x64.h
js/src/jit/x64/LOpcodes-x64.h
js/src/jit/x64/Lowering-x64.cpp
js/src/jit/x64/Lowering-x64.h
js/src/jit/x86/Lowering-x86.cpp
js/src/jit/x86/Lowering-x86.h
--- a/js/src/asmjs/Wasm.cpp
+++ b/js/src/asmjs/Wasm.cpp
@@ -605,23 +605,22 @@ DecodeExpr(FunctionDecoder& f, ExprType 
       case Expr::I32TruncSF64:
       case Expr::I32TruncUF64:
         return DecodeConversionOperator(f, expected, ExprType::I32, ExprType::F64);
       case Expr::I64ExtendSI32:
       case Expr::I64ExtendUI32:
         return DecodeConversionOperator(f, expected, ExprType::I64, ExprType::I32);
       case Expr::I64TruncSF32:
       case Expr::I64TruncUF32:
-        return f.fail("NYI: i64") &&
-               DecodeConversionOperator(f, expected, ExprType::I64, ExprType::F32);
+        return DecodeConversionOperator(f, expected, ExprType::I64, ExprType::F32);
       case Expr::I64TruncSF64:
       case Expr::I64TruncUF64:
+        return DecodeConversionOperator(f, expected, ExprType::I64, ExprType::F64);
       case Expr::I64ReinterpretF64:
-        return f.fail("NYI: i64") &&
-               DecodeConversionOperator(f, expected, ExprType::I64, ExprType::F64);
+        return f.fail("NYI: i64");
       case Expr::F32ConvertSI32:
       case Expr::F32ConvertUI32:
         return DecodeConversionOperator(f, expected, ExprType::F32, ExprType::I32);
       case Expr::F32ReinterpretI32:
         return f.fail("NYI: reinterpret");
       case Expr::F32ConvertSI64:
       case Expr::F32ConvertUI64:
         return f.fail("NYI: i64") &&
--- a/js/src/asmjs/WasmIonCompile.cpp
+++ b/js/src/asmjs/WasmIonCompile.cpp
@@ -456,16 +456,26 @@ class FunctionCompiler
     {
         if (inDeadCode())
             return nullptr;
         MExtendInt32ToInt64* ins = MExtendInt32ToInt64::NewAsmJS(alloc(), op, isUnsigned);
         curBlock_->add(ins);
         return ins;
     }
 
+    template <class T>
+    MDefinition* truncate(MDefinition* op, bool isUnsigned)
+    {
+        if (inDeadCode())
+            return nullptr;
+        T* ins = T::NewAsmJS(alloc(), op, isUnsigned);
+        curBlock_->add(ins);
+        return ins;
+    }
+
     MDefinition* compare(MDefinition* lhs, MDefinition* rhs, JSOp op, MCompare::CompareType type)
     {
         if (inDeadCode())
             return nullptr;
         MCompare* ins = MCompare::NewAsmJS(alloc(), lhs, rhs, op, type);
         curBlock_->add(ins);
         return ins;
     }
@@ -2299,16 +2309,27 @@ EmitExtendI32(FunctionCompiler& f, bool 
 {
     MDefinition* in;
     if (!EmitExpr(f, ExprType::I32, &in))
         return false;
     *def = f.extendI32(in, isUnsigned);
     return true;
 }
 
+template<class T>
+static bool
+EmitTruncate(FunctionCompiler& f, ExprType type, bool isUnsigned, MDefinition** def)
+{
+    MDefinition* in;
+    if (!EmitExpr(f, type, &in))
+        return false;
+    *def = f.truncate<T>(in, isUnsigned);
+    return true;
+}
+
 static bool
 EmitSimdOp(FunctionCompiler& f, ExprType type, SimdOperation op, SimdSign sign, MDefinition** def)
 {
     switch (op) {
       case SimdOperation::Constructor:
         return EmitSimdCtor(f, type, def);
       case SimdOperation::Fn_extractLane:
         return EmitExtractLane(f, type, sign, def);
@@ -2747,16 +2768,24 @@ EmitExpr(FunctionCompiler& f, ExprType t
       case Expr::I32AtomicsBinOp:
         return EmitAtomicsBinOp(f, def);
       // I64
       case Expr::I64Const:
         return EmitLiteral(f, ExprType::I64, def);
       case Expr::I64ExtendSI32:
       case Expr::I64ExtendUI32:
         return EmitExtendI32(f, IsUnsigned(op == Expr::I64ExtendUI32), def);
+      case Expr::I64TruncSF32:
+      case Expr::I64TruncUF32:
+        return EmitTruncate<MTruncateToInt64>(f, ExprType::F32,
+                                              IsUnsigned(op == Expr::I64TruncUF32), def);
+      case Expr::I64TruncSF64:
+      case Expr::I64TruncUF64:
+        return EmitTruncate<MTruncateToInt64>(f, ExprType::F64,
+                                              IsUnsigned(op == Expr::I64TruncUF64), def);
       case Expr::I64Or:
         return EmitBitwise<MBitOr>(f, ExprType::I64, def);
       case Expr::I64And:
         return EmitBitwise<MBitAnd>(f, ExprType::I64, def);
       case Expr::I64Xor:
         return EmitBitwise<MBitXor>(f, ExprType::I64, def);
       case Expr::I64Shl:
         return EmitBitwise<MLsh>(f, ExprType::I64, def);
@@ -2906,20 +2935,16 @@ EmitExpr(FunctionCompiler& f, ExprType t
       // Future opcodes
       case Expr::Select:
       case Expr::F32CopySign:
       case Expr::F32Trunc:
       case Expr::F32Nearest:
       case Expr::F64CopySign:
       case Expr::F64Nearest:
       case Expr::F64Trunc:
-      case Expr::I64TruncSF32:
-      case Expr::I64TruncSF64:
-      case Expr::I64TruncUF32:
-      case Expr::I64TruncUF64:
       case Expr::F32ConvertSI64:
       case Expr::F32ConvertUI64:
       case Expr::F64ConvertSI64:
       case Expr::F64ConvertUI64:
       case Expr::I64ReinterpretF64:
       case Expr::F64ReinterpretI64:
       case Expr::I32ReinterpretF32:
       case Expr::F32ReinterpretI32:
--- a/js/src/jit-test/tests/wasm/basic-conversion.js
+++ b/js/src/jit-test/tests/wasm/basic-conversion.js
@@ -59,16 +59,102 @@ if (getBuildConfiguration().x64) {
     testConversion('i64', 'extend_s', 'i32', 0x80000000, "0xffffffff80000000");
 
     testConversion('i64', 'extend_u', 'i32', 0, 0);
     testConversion('i64', 'extend_u', 'i32', 1234, 1234);
     testConversion('i64', 'extend_u', 'i32', -567, "0x00000000fffffdc9");
     testConversion('i64', 'extend_u', 'i32', -1, "0x00000000ffffffff");
     testConversion('i64', 'extend_u', 'i32', 0x7fffffff, "0x000000007fffffff");
     testConversion('i64', 'extend_u', 'i32', 0x80000000, "0x0000000080000000");
+
+    testConversion('i64', 'trunc_s', 'f64', 0.0, 0);
+    testConversion('i64', 'trunc_s', 'f64', "-0.0", 0);
+    testConversion('i64', 'trunc_s', 'f64', 1.0, 1);
+    testConversion('i64', 'trunc_s', 'f64', 1.1, 1);
+    testConversion('i64', 'trunc_s', 'f64', 1.5, 1);
+    testConversion('i64', 'trunc_s', 'f64', 1.99, 1);
+    testConversion('i64', 'trunc_s', 'f64', 40.1, 40);
+    testConversion('i64', 'trunc_s', 'f64', -1.0, -1);
+    testConversion('i64', 'trunc_s', 'f64', -1.1, -1);
+    testConversion('i64', 'trunc_s', 'f64', -1.5, -1);
+    testConversion('i64', 'trunc_s', 'f64', -1.99, -1);
+    testConversion('i64', 'trunc_s', 'f64', -2.0, -2);
+    testConversion('i64', 'trunc_s', 'f64', 4294967296.1, "4294967296");
+    testConversion('i64', 'trunc_s', 'f64', -4294967296.8, "-4294967296");
+    testConversion('i64', 'trunc_s', 'f64', 9223372036854774784.8, "9223372036854774784");
+    testConversion('i64', 'trunc_s', 'f64', -9223372036854775808.3, "-9223372036854775808");
+
+    testConversion('i64', 'trunc_u', 'f64', 0.0, 0);
+    testConversion('i64', 'trunc_u', 'f64', "-0.0", 0);
+    testConversion('i64', 'trunc_u', 'f64', 1.0, 1);
+    testConversion('i64', 'trunc_u', 'f64', 1.1, 1);
+    testConversion('i64', 'trunc_u', 'f64', 1.5, 1);
+    testConversion('i64', 'trunc_u', 'f64', 1.99, 1);
+    testConversion('i64', 'trunc_u', 'f64', -0.9, 0);
+    testConversion('i64', 'trunc_u', 'f64', 40.1, 40);
+    testConversion('i64', 'trunc_u', 'f64', 4294967295, "0xffffffff");
+    testConversion('i64', 'trunc_u', 'f64', 4294967296.1, "4294967296");
+    testConversion('i64', 'trunc_u', 'f64', 1e8, "100000000");
+    testConversion('i64', 'trunc_u', 'f64', 1e16, "10000000000000000");
+    testConversion('i64', 'trunc_u', 'f64', 9223372036854775808, "-9223372036854775808");
+    testConversion('i64', 'trunc_u', 'f64', 18446744073709549568.1, -2048);
+
+    testConversion('i64', 'trunc_s', 'f32', 0.0, 0);
+    testConversion('i64', 'trunc_s', 'f32', "-0.0", 0);
+    testConversion('i64', 'trunc_s', 'f32', 1.0, 1);
+    testConversion('i64', 'trunc_s', 'f32', 1.1, 1);
+    testConversion('i64', 'trunc_s', 'f32', 1.5, 1);
+    testConversion('i64', 'trunc_s', 'f32', 1.99, 1);
+    testConversion('i64', 'trunc_s', 'f32', 40.1, 40);
+    testConversion('i64', 'trunc_s', 'f32', -1.0, -1);
+    testConversion('i64', 'trunc_s', 'f32', -1.1, -1);
+    testConversion('i64', 'trunc_s', 'f32', -1.5, -1);
+    testConversion('i64', 'trunc_s', 'f32', -1.99, -1);
+    testConversion('i64', 'trunc_s', 'f32', -2.0, -2);
+    testConversion('i64', 'trunc_s', 'f32', 4294967296.1, "4294967296");
+    testConversion('i64', 'trunc_s', 'f32', -4294967296.8, "-4294967296");
+    testConversion('i64', 'trunc_s', 'f32', 9223371487098961920.0, "9223371487098961920");
+    testConversion('i64', 'trunc_s', 'f32', -9223372036854775808.3, "-9223372036854775808");
+
+    testConversion('i64', 'trunc_u', 'f32', 0.0, 0);
+    testConversion('i64', 'trunc_u', 'f32', "-0.0", 0);
+    testConversion('i64', 'trunc_u', 'f32', 1.0, 1);
+    testConversion('i64', 'trunc_u', 'f32', 1.1, 1);
+    testConversion('i64', 'trunc_u', 'f32', 1.5, 1);
+    testConversion('i64', 'trunc_u', 'f32', 1.99, 1);
+    testConversion('i64', 'trunc_u', 'f32', -0.9, 0);
+    testConversion('i64', 'trunc_u', 'f32', 40.1, 40);
+    testConversion('i64', 'trunc_u', 'f32', 1e8, "100000000");
+    testConversion('i64', 'trunc_u', 'f32', 4294967296, "4294967296");
+    testConversion('i64', 'trunc_u', 'f32', 18446742974197923840.0, "-1099511627776");
+
+    // TODO: these should trap.
+    testConversion('i64', 'trunc_s', 'f64', 9223372036854775808.0, "0x8000000000000000");
+    testConversion('i64', 'trunc_s', 'f64', -9223372036854777856.0, "0x8000000000000000");
+    testConversion('i64', 'trunc_s', 'f64', "nan", "0x8000000000000000");
+    testConversion('i64', 'trunc_s', 'f64', "infinity", "0x8000000000000000");
+    testConversion('i64', 'trunc_s', 'f64', "-infinity", "0x8000000000000000");
+
+    testConversion('i64', 'trunc_u', 'f64', -1, "0x8000000000000000");
+    testConversion('i64', 'trunc_u', 'f64', 18446744073709551616.0, "0x8000000000000000");
+    testConversion('i64', 'trunc_u', 'f64', "nan", "0x8000000000000000");
+    testConversion('i64', 'trunc_u', 'f64', "infinity", "0x8000000000000000");
+    testConversion('i64', 'trunc_u', 'f64', "-infinity", "0x8000000000000000");
+
+    testConversion('i64', 'trunc_s', 'f32', 9223372036854775808.0, "0x8000000000000000");
+    testConversion('i64', 'trunc_s', 'f32', -9223372036854777856.0, "0x8000000000000000");
+    testConversion('i64', 'trunc_s', 'f32', "nan", "0x8000000000000000");
+    testConversion('i64', 'trunc_s', 'f32', "infinity", "0x8000000000000000");
+    testConversion('i64', 'trunc_s', 'f32', "-infinity", "0x8000000000000000");
+
+    testConversion('i64', 'trunc_u', 'f32', 18446744073709551616.0, "0x8000000000000000");
+    testConversion('i64', 'trunc_u', 'f32', -1, "0x8000000000000000");
+    testConversion('i64', 'trunc_u', 'f32', "nan", "0x8000000000000000");
+    testConversion('i64', 'trunc_u', 'f32', "infinity", "0x8000000000000000");
+    testConversion('i64', 'trunc_u', 'f32', "-infinity", "0x8000000000000000");
 } else {
     // Sleeper test: once i64 works on more platforms, remove this if-else.
     try {
         testConversion('i32', 'wrap', 'i64', 4294967336, 40);
         assertEq(0, 1);
     } catch(e) {
         assertEq(e.toString().indexOf("NYI on this platform") >= 0, true);
     }
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -5303,16 +5303,50 @@ class MExtendInt32ToInt64
             return false;
         return congruentIfOperandsEqual(ins);
     }
     AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
 };
 
+class MTruncateToInt64
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
+{
+    bool isUnsigned_;
+
+    MTruncateToInt64(MDefinition* def, bool isUnsigned)
+      : MUnaryInstruction(def),
+        isUnsigned_(isUnsigned)
+    {
+        setResultType(MIRType_Int64);
+        setMovable();
+    }
+
+  public:
+    INSTRUCTION_HEADER(TruncateToInt64)
+    static MTruncateToInt64* NewAsmJS(TempAllocator& alloc, MDefinition* def, bool isUnsigned) {
+        return new(alloc) MTruncateToInt64(def, isUnsigned);
+    }
+
+    bool isUnsigned() const { return isUnsigned_; }
+
+    bool congruentTo(const MDefinition* ins) const override {
+        if (!ins->isTruncateToInt64())
+            return false;
+        if (ins->toTruncateToInt64()->isUnsigned_ != isUnsigned_)
+            return false;
+        return congruentIfOperandsEqual(ins);
+    }
+    AliasSet getAliasSet() const override {
+        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 ToInt32Policy::Data
 {
     bool canBeNegativeZero_;
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -113,16 +113,17 @@ namespace jit {
     _(GuardObject)                                                          \
     _(GuardString)                                                          \
     _(PolyInlineGuard)                                                      \
     _(AssertRange)                                                          \
     _(ToDouble)                                                             \
     _(ToFloat32)                                                            \
     _(ToInt32)                                                              \
     _(TruncateToInt32)                                                      \
+    _(TruncateToInt64)                                                      \
     _(WrapInt64ToInt32)                                                     \
     _(ExtendInt32ToInt64)                                                   \
     _(ToString)                                                             \
     _(ToObjectOrNull)                                                       \
     _(NewArray)                                                             \
     _(NewArrayCopyOnWrite)                                                  \
     _(NewArrayDynamicLength)                                                \
     _(NewObject)                                                            \
--- a/js/src/jit/arm/Lowering-arm.cpp
+++ b/js/src/jit/arm/Lowering-arm.cpp
@@ -768,8 +768,14 @@ LIRGeneratorARM::visitRandom(MRandom* in
 {
     LRandom *lir = new(alloc()) LRandom(temp(),
                                         temp(),
                                         temp(),
                                         temp(),
                                         temp());
     defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
 }
+
+void
+LIRGeneratorARM::visitTruncateToInt64(MTruncateToInt64* ins)
+{
+    MOZ_CRASH("NY");
+}
--- a/js/src/jit/arm/Lowering-arm.h
+++ b/js/src/jit/arm/Lowering-arm.h
@@ -107,16 +107,17 @@ class LIRGeneratorARM : public LIRGenera
     void visitSimdSelect(MSimdSelect* ins);
     void visitSimdSplatX4(MSimdSplatX4* ins);
     void visitSimdValueX4(MSimdValueX4* ins);
     void visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins);
     void visitAtomicExchangeTypedArrayElement(MAtomicExchangeTypedArrayElement* ins);
     void visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins);
     void visitSubstr(MSubstr* ins);
     void visitRandom(MRandom* ins);
+    void visitTruncateToInt64(MTruncateToInt64* ins);
 };
 
 typedef LIRGeneratorARM LIRGeneratorSpecific;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_arm_Lowering_arm_h */
--- a/js/src/jit/arm64/Lowering-arm64.cpp
+++ b/js/src/jit/arm64/Lowering-arm64.cpp
@@ -330,8 +330,14 @@ LIRGeneratorARM64::visitSubstr(MSubstr* 
 void
 LIRGeneratorARM64::visitRandom(MRandom* ins)
 {
     LRandom *lir = new(alloc()) LRandom(temp(),
                                         temp(),
                                         temp());
     defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
 }
+
+void
+LIRGeneratorARM64::visitTruncateToInt64(MTruncateToInt64* ins)
+{
+    MOZ_CRASH("NY");
+}
--- a/js/src/jit/arm64/Lowering-arm64.h
+++ b/js/src/jit/arm64/Lowering-arm64.h
@@ -110,16 +110,17 @@ class LIRGeneratorARM64 : public LIRGene
     void visitSimdSelect(MSimdSelect* ins);
     void visitSimdSplatX4(MSimdSplatX4* ins);
     void visitSimdValueX4(MSimdValueX4* ins);
     void visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins);
     void visitAtomicExchangeTypedArrayElement(MAtomicExchangeTypedArrayElement* ins);
     void visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins);
     void visitSubstr(MSubstr* ins);
     void visitRandom(MRandom* ins);
+    void visitTruncateToInt64(MTruncateToInt64* ins);
 };
 
 typedef LIRGeneratorARM64 LIRGeneratorSpecific;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_arm64_Lowering_arm64_h */
--- a/js/src/jit/mips-shared/Lowering-mips-shared.cpp
+++ b/js/src/jit/mips-shared/Lowering-mips-shared.cpp
@@ -569,8 +569,14 @@ LIRGeneratorMIPSShared::visitAtomicTyped
     // On mips, map flagTemp to temp1 and outTemp to temp2, at least for now.
 
     LAtomicTypedArrayElementBinop* lir =
         new(alloc()) LAtomicTypedArrayElementBinop(elements, index, value, flagTemp, outTemp,
                                                    /* valueTemp= */ temp(), /* offsetTemp= */ temp(),
                                                    /* maskTemp= */ temp());
     define(lir, ins);
 }
+
+void
+LIRGeneratorMIPSShared::visitTruncateToInt64(MTruncateToInt64* ins)
+{
+    MOZ_CRASH("NY");
+}
--- a/js/src/jit/mips-shared/Lowering-mips-shared.h
+++ b/js/src/jit/mips-shared/Lowering-mips-shared.h
@@ -91,14 +91,15 @@ class LIRGeneratorMIPSShared : public LI
     void visitSimdBinaryArith(MSimdBinaryArith* ins);
     void visitSimdSelect(MSimdSelect* ins);
     void visitSimdSplatX4(MSimdSplatX4* ins);
     void visitSimdValueX4(MSimdValueX4* ins);
     void visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins);
     void visitAtomicExchangeTypedArrayElement(MAtomicExchangeTypedArrayElement* ins);
     void visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins);
     void visitSubstr(MSubstr* ins);
+    void visitTruncateToInt64(MTruncateToInt64* ins);
 };
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_mips_shared_Lowering_mips_shared_h */
--- a/js/src/jit/none/Lowering-none.h
+++ b/js/src/jit/none/Lowering-none.h
@@ -89,17 +89,17 @@ class LIRGeneratorNone : public LIRGener
     LTableSwitch* newLTableSwitch(LAllocation, LDefinition, MTableSwitch*) { MOZ_CRASH(); }
     LTableSwitchV* newLTableSwitchV(MTableSwitch*) { MOZ_CRASH(); }
     void visitSimdSelect(MSimdSelect* ins) { MOZ_CRASH(); }
     void visitSimdSplatX4(MSimdSplatX4* ins) { MOZ_CRASH(); }
     void visitSimdValueX4(MSimdValueX4* lir) { MOZ_CRASH(); }
     void visitSubstr(MSubstr*) { MOZ_CRASH(); }
     void visitSimdBinaryArith(js::jit::MSimdBinaryArith*) { MOZ_CRASH(); }
     void visitRandom(js::jit::MRandom*) { MOZ_CRASH(); }
-
+    void visitTruncateToInt64(MTruncateToInt64*) { MOZ_CRASH(); }
 };
 
 typedef LIRGeneratorNone LIRGeneratorSpecific;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_none_Lowering_none_h */
--- a/js/src/jit/x64/CodeGenerator-x64.cpp
+++ b/js/src/jit/x64/CodeGenerator-x64.cpp
@@ -1107,8 +1107,83 @@ CodeGeneratorX64::visitExtendInt32ToInt6
     const LAllocation* input = lir->getOperand(0);
     Register output = ToRegister(lir->output());
 
     if (lir->mir()->isUnsigned())
         masm.movl(ToOperand(input), output);
     else
         masm.movslq(ToOperand(input), output);
 }
+
+void
+CodeGeneratorX64::visitTruncateToInt64(LTruncateToInt64* lir)
+{
+    FloatRegister input = ToFloatRegister(lir->input());
+    Register output = ToRegister(lir->output());
+
+    MIRType inputType = lir->mir()->input()->type();
+    bool isUnsigned = lir->mir()->isUnsigned();
+
+    // We should trap on invalid inputs, but for now we just return
+    // 0x8000000000000000. Note that we can remove some unnecessary jumps
+    // once we get rid of this trap Label.
+    Label trap;
+
+    Label done;
+    if (isUnsigned) {
+        FloatRegister tempDouble = ToFloatRegister(lir->temp());
+
+        // If the input < INT64_MAX, vcvttsd2sq will do the right thing, so
+        // we use it directly. Else, we subtract INT64_MAX, convert to int64,
+        // and then add INT64_MAX to the result.
+        if (inputType == MIRType_Double) {
+            Label isLarge;
+            masm.loadConstantDouble(double(0x8000000000000000), ScratchDoubleReg);
+            masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, input, ScratchDoubleReg, &isLarge);
+            masm.vcvttsd2sq(input, output);
+            masm.branchTestPtr(Assembler::Signed, output, output, &trap);
+            masm.jump(&done);
+
+            masm.bind(&isLarge);
+            masm.moveDouble(input, tempDouble);
+            masm.subDouble(ScratchDoubleReg, tempDouble);
+            masm.vcvttsd2sq(tempDouble, output);
+            masm.branchTestPtr(Assembler::Signed, output, output, &trap);
+            masm.or64(Imm64(0x8000000000000000), Register64(output));
+            masm.jump(&done);
+        } else {
+            MOZ_ASSERT(inputType == MIRType_Float32);
+
+            Label isLarge;
+            masm.loadConstantFloat32(float(0x8000000000000000), ScratchDoubleReg);
+            masm.branchFloat(Assembler::DoubleGreaterThanOrEqual, input, ScratchDoubleReg, &isLarge);
+            masm.vcvttss2sq(input, output);
+            masm.branchTestPtr(Assembler::Signed, output, output, &trap);
+            masm.jump(&done);
+
+            masm.bind(&isLarge);
+            masm.moveFloat32(input, tempDouble);
+            masm.vsubss(ScratchDoubleReg, tempDouble, tempDouble);
+            masm.vcvttss2sq(tempDouble, output);
+            masm.branchTestPtr(Assembler::Signed, output, output, &trap);
+            masm.or64(Imm64(0x8000000000000000), Register64(output));
+            masm.jump(&done);
+        }
+    } else {
+        if (inputType == MIRType_Double) {
+            masm.vcvttsd2sq(input, output);
+            masm.cmpq(Imm32(1), output);
+            masm.j(Assembler::Overflow, &trap);
+            masm.jump(&done);
+        } else {
+            MOZ_ASSERT(inputType == MIRType_Float32);
+            masm.vcvttss2sq(input, output);
+            masm.cmpq(Imm32(1), output);
+            masm.j(Assembler::Overflow, &trap);
+            masm.jump(&done);
+        }
+    }
+
+    masm.bind(&trap);
+    masm.movePtr(ImmWord(0x8000000000000000), output);
+
+    masm.bind(&done);
+}
--- a/js/src/jit/x64/CodeGenerator-x64.h
+++ b/js/src/jit/x64/CodeGenerator-x64.h
@@ -50,16 +50,17 @@ class CodeGeneratorX64 : public CodeGene
     void visitSubI64(LSubI64* lir);
     void visitMulI64(LMulI64* lir);
     void visitDivOrModI64(LDivOrModI64* lir);
     void visitUDivOrMod64(LUDivOrMod64* lir);
     void visitTruncateDToInt32(LTruncateDToInt32* ins);
     void visitTruncateFToInt32(LTruncateFToInt32* ins);
     void visitWrapInt64ToInt32(LWrapInt64ToInt32* lir);
     void visitExtendInt32ToInt64(LExtendInt32ToInt64* lir);
+    void visitTruncateToInt64(LTruncateToInt64* lir);
     void visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins);
     void visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins);
     void visitAsmJSCall(LAsmJSCall* ins);
     void visitAsmJSLoadHeap(LAsmJSLoadHeap* ins);
     void visitAsmJSStoreHeap(LAsmJSStoreHeap* ins);
     void visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins);
     void visitAsmJSAtomicExchangeHeap(LAsmJSAtomicExchangeHeap* ins);
     void visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins);
--- a/js/src/jit/x64/LIR-x64.h
+++ b/js/src/jit/x64/LIR-x64.h
@@ -159,12 +159,31 @@ class LUDivOrMod64 : public LBinaryMath<
 
     bool canBeDivideByZero() const {
         if (mir_->isMod())
             return mir_->toMod()->canBeDivideByZero();
         return mir_->toDiv()->canBeDivideByZero();
     }
 };
 
+class LTruncateToInt64 : public LInstructionHelper<1, 1, 1>
+{
+  public:
+    LIR_HEADER(TruncateToInt64);
+
+    LTruncateToInt64(const LAllocation& in, const LDefinition& temp) {
+        setOperand(0, in);
+        setTemp(0, temp);
+    }
+
+    MTruncateToInt64* mir() const {
+        return mir_->toTruncateToInt64();
+    }
+
+    const LDefinition* temp() {
+        return getTemp(0);
+    }
+};
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_x64_LIR_x64_h */
--- a/js/src/jit/x64/LOpcodes-x64.h
+++ b/js/src/jit/x64/LOpcodes-x64.h
@@ -8,14 +8,15 @@
 #define jit_x64_LOpcodes_x64_h
 
 #include "jit/shared/LOpcodes-shared.h"
 
 #define LIR_CPU_OPCODE_LIST(_)      \
     _(DivOrModConstantI)            \
     _(DivOrModI64)                  \
     _(UDivOrMod64)                  \
+    _(TruncateToInt64)              \
     _(SimdValueInt32x4)             \
     _(SimdValueFloat32x4)           \
     _(UDivOrMod)                    \
     _(UDivOrModConstant)
 
 #endif /* jit_x64_LOpcodes_x64_h */
--- a/js/src/jit/x64/Lowering-x64.cpp
+++ b/js/src/jit/x64/Lowering-x64.cpp
@@ -380,8 +380,18 @@ LIRGeneratorX64::lowerUDiv64(MDiv* div)
 void
 LIRGeneratorX64::lowerUMod64(MMod* mod)
 {
     LUDivOrMod64* lir = new(alloc()) LUDivOrMod64(useRegister(mod->lhs()),
                                                   useRegister(mod->rhs()),
                                                   tempFixed(rax));
     defineInt64Fixed(lir, mod, LInt64Allocation(LAllocation(AnyRegister(rdx))));
 }
+
+void
+LIRGeneratorX64::visitTruncateToInt64(MTruncateToInt64* ins)
+{
+    MDefinition* opd = ins->input();
+    MOZ_ASSERT(opd->type() == MIRType_Double || opd->type() == MIRType_Float32);
+
+    LDefinition maybeTemp = ins->isUnsigned() ? tempDouble() : LDefinition::BogusTemp();
+    defineInt64(new(alloc()) LTruncateToInt64(useRegister(opd), maybeTemp), ins);
+}
--- a/js/src/jit/x64/Lowering-x64.h
+++ b/js/src/jit/x64/Lowering-x64.h
@@ -54,16 +54,17 @@ class LIRGeneratorX64 : public LIRGenera
     void visitAsmJSStoreHeap(MAsmJSStoreHeap* ins);
     void visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr* ins);
     void visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap* ins);
     void visitAsmJSAtomicExchangeHeap(MAsmJSAtomicExchangeHeap* ins);
     void visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins);
     void visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins);
     void visitSubstr(MSubstr* ins);
     void visitRandom(MRandom* ins);
+    void visitTruncateToInt64(MTruncateToInt64* ins);
 };
 
 typedef LIRGeneratorX64 LIRGeneratorSpecific;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_x64_Lowering_x64_h */
--- a/js/src/jit/x86/Lowering-x86.cpp
+++ b/js/src/jit/x86/Lowering-x86.cpp
@@ -453,8 +453,14 @@ LIRGeneratorX86::visitRandom(MRandom* in
 {
     LRandom *lir = new(alloc()) LRandom(temp(),
                                         temp(),
                                         temp(),
                                         temp(),
                                         temp());
     defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
 }
+
+void
+LIRGeneratorX86::visitTruncateToInt64(MTruncateToInt64* ins)
+{
+    MOZ_CRASH("NY");
+}
--- a/js/src/jit/x86/Lowering-x86.h
+++ b/js/src/jit/x86/Lowering-x86.h
@@ -58,16 +58,17 @@ class LIRGeneratorX86 : public LIRGenera
     void visitAsmJSStoreHeap(MAsmJSStoreHeap* ins);
     void visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr* ins);
     void visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap* ins);
     void visitAsmJSAtomicExchangeHeap(MAsmJSAtomicExchangeHeap* ins);
     void visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins);
     void visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins);
     void visitSubstr(MSubstr* ins);
     void visitRandom(MRandom* ins);
+    void visitTruncateToInt64(MTruncateToInt64* ins);
     void lowerPhi(MPhi* phi);
 
     static bool allowTypedElementHoleCheck() {
         return true;
     }
 
     static bool allowStaticTypedArrayAccesses() {
         return true;