Bug 1251225 - Implement wasm i64 binary arithmetic operators. r=sunfish
authorJan de Mooij <jdemooij@mozilla.com>
Tue, 01 Mar 2016 14:20:45 +0100
changeset 286272 02e9d9afda63b8da07cf28a3d55a8e680968ec9a
parent 286271 df6847768408ed5a157913f2db8b23c495549540
child 286273 5f7418902a4aba07c8bb60c6da1623b5dc2f981e
push id30043
push userkwierso@gmail.com
push dateTue, 01 Mar 2016 23:22:33 +0000
treeherdermozilla-central@eb25b90a05c1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssunfish
bugs1251225
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 1251225 - Implement wasm i64 binary arithmetic operators. r=sunfish
js/src/asmjs/Wasm.cpp
js/src/asmjs/WasmIonCompile.cpp
js/src/jit-test/tests/wasm/basic-integer.js
js/src/jit/Lowering.cpp
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jit/MacroAssembler.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/shared/LIR-shared.h
js/src/jit/shared/LOpcodes-shared.h
js/src/jit/shared/Lowering-shared-inl.h
js/src/jit/shared/Lowering-shared.h
js/src/jit/x64/Assembler-x64.h
js/src/jit/x64/BaseAssembler-x64.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/x64/MacroAssembler-x64-inl.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
@@ -473,17 +473,16 @@ DecodeExpr(FunctionDecoder& f, ExprType 
         return DecodeBinaryOperator(f, expected, ExprType::I32);
       case Expr::I64Add:
       case Expr::I64Sub:
       case Expr::I64Mul:
       case Expr::I64DivS:
       case Expr::I64DivU:
       case Expr::I64RemS:
       case Expr::I64RemU:
-        return f.fail("NYI: i64");
       case Expr::I64And:
       case Expr::I64Or:
       case Expr::I64Xor:
       case Expr::I64Shl:
       case Expr::I64ShrS:
       case Expr::I64ShrU:
         return DecodeBinaryOperator(f, expected, ExprType::I64);
       case Expr::F32Add:
--- a/js/src/asmjs/WasmIonCompile.cpp
+++ b/js/src/asmjs/WasmIonCompile.cpp
@@ -2194,17 +2194,18 @@ EmitDivOrMod(FunctionCompiler& f, ExprTy
            ? f.div(lhs, rhs, ToMIRType(type), isUnsigned)
            : f.mod(lhs, rhs, ToMIRType(type), isUnsigned);
     return true;
 }
 
 static bool
 EmitDivOrMod(FunctionCompiler& f, ExprType type, bool isDiv, MDefinition** def)
 {
-    MOZ_ASSERT(type != ExprType::I32, "int div or mod must precise signedness");
+    MOZ_ASSERT(type != ExprType::I32 && type != ExprType::I64,
+               "int div or mod must indicate signedness");
     return EmitDivOrMod(f, type, isDiv, false, def);
 }
 
 static bool
 EmitComparison(FunctionCompiler& f, Expr expr, MDefinition** def)
 {
     MDefinition *lhs, *rhs;
     MCompare::CompareType compareType;
@@ -2907,16 +2908,28 @@ EmitExpr(FunctionCompiler& f, ExprType t
       case Expr::I64Xor:
         return EmitBitwise<MBitXor>(f, ExprType::I64, def);
       case Expr::I64Shl:
         return EmitBitwise<MLsh>(f, ExprType::I64, def);
       case Expr::I64ShrS:
         return EmitBitwise<MRsh>(f, ExprType::I64, def);
       case Expr::I64ShrU:
         return EmitBitwise<MUrsh>(f, ExprType::I64, def);
+      case Expr::I64Add:
+        return EmitAddOrSub(f, ExprType::I64, IsAdd(true), def);
+      case Expr::I64Sub:
+        return EmitAddOrSub(f, ExprType::I64, IsAdd(false), def);
+      case Expr::I64Mul:
+        return EmitMultiply(f, ExprType::I64, def);
+      case Expr::I64DivS:
+      case Expr::I64DivU:
+        return EmitDivOrMod(f, ExprType::I64, IsDiv(true), IsUnsigned(op == Expr::I64DivU), def);
+      case Expr::I64RemS:
+      case Expr::I64RemU:
+        return EmitDivOrMod(f, ExprType::I64, IsDiv(false), IsUnsigned(op == Expr::I64RemU), def);
       // F32
       case Expr::F32Const:
         return EmitLiteral(f, ExprType::F32, def);
       case Expr::F32Add:
         return EmitAddOrSub(f, ExprType::F32, IsAdd(true), def);
       case Expr::F32Sub:
         return EmitAddOrSub(f, ExprType::F32, IsAdd(false), def);
       case Expr::F32Mul:
@@ -3076,23 +3089,16 @@ EmitExpr(FunctionCompiler& f, ExprType t
       case Expr::I64LoadMem:
       case Expr::I64StoreMem8:
       case Expr::I64StoreMem16:
       case Expr::I64StoreMem32:
       case Expr::I64StoreMem:
       case Expr::I64Clz:
       case Expr::I64Ctz:
       case Expr::I64Popcnt:
-      case Expr::I64Add:
-      case Expr::I64Sub:
-      case Expr::I64Mul:
-      case Expr::I64DivS:
-      case Expr::I64DivU:
-      case Expr::I64RemS:
-      case Expr::I64RemU:
         MOZ_CRASH("NYI");
       case Expr::Unreachable:
         break;
       case Expr::Limit:
         MOZ_CRASH("Limit");
     }
 
     MOZ_CRASH("unexpected wasm opcode");
--- a/js/src/jit-test/tests/wasm/basic-integer.js
+++ b/js/src/jit-test/tests/wasm/basic-integer.js
@@ -78,25 +78,48 @@ testComparison('i32', 'gt_s', 40, 40, 0)
 testComparison('i32', 'gt_u', 40, 40, 0);
 testComparison('i32', 'ge_s', 40, 40, 1);
 testComparison('i32', 'ge_u', 40, 40, 1);
 
 //testUnary('i64', 'clz', 40, 58); // TODO: NYI
 //testUnary('i64', 'ctz', 40, 0); // TODO: NYI
 //testUnary('i64', 'popcnt', 40, 0); // TODO: NYI
 
-//testBinary('i64', 'add', 40, 2, 42); // TODO: NYI
-//testBinary('i64', 'sub', 40, 2, 38); // TODO: NYI
-//testBinary('i64', 'mul', 40, 2, 80); // TODO: NYI
-//testBinary('i64', 'div_s', -40, 2, -20); // TODO: NYI
-//testBinary('i64', 'div_u', -40, 2, 2147483628); // TODO: NYI
-//testBinary('i64', 'rem_s', 40, -3, 1); // TODO: NYI
-//testBinary('i64', 'rem_u', 40, -3, 40); // TODO: NYI
+if (getBuildConfiguration().x64) {
+    testBinary('i64', 'add', 40, 2, 42);
+    testBinary('i64', 'add', "0x1234567887654321", -1, "0x1234567887654320");
+    testBinary('i64', 'add', "0xffffffffffffffff", 1, 0);
+    testBinary('i64', 'sub', 40, 2, 38);
+    testBinary('i64', 'sub', "0x1234567887654321", "0x123456789", "0x12345677641fdb98");
+    testBinary('i64', 'sub', 3, 5, -2);
+    testBinary('i64', 'mul', 40, 2, 80);
+    testBinary('i64', 'mul', -1, 2, -2);
+    testBinary('i64', 'mul', 0x123456, "0x9876543210", "0xad77d2c5f941160");
+    testBinary('i64', 'div_s', -40, 2, -20);
+    testBinary('i64', 'div_s', "0x1234567887654321", 2, "0x91a2b3c43b2a190");
+    testBinary('i64', 'div_s', "0x1234567887654321", "0x1000000000", "0x1234567");
+    testBinary('i64', 'div_u', -40, 2, "0x7fffffffffffffec");
+    testBinary('i64', 'div_u', "0x1234567887654321", 9, "0x205d0b80f0b4059");
+    testBinary('i64', 'rem_s', 40, -3, 1);
+    testBinary('i64', 'rem_s', "0x1234567887654321", "0x1000000000", "0x887654321");
+    testBinary('i64', 'rem_s', "0x7fffffffffffffff", -1, 0);
+    testBinary('i64', 'rem_s', "0x8000000000000001", 1000, -807);
+    testBinary('i64', 'rem_s', "0x8000000000000000", -1, 0);
+    testBinary('i64', 'rem_u', 40, -3, 40);
+    testBinary('i64', 'rem_u', "0x1234567887654321", "0x1000000000", "0x887654321");
+    testBinary('i64', 'rem_u', "0x8000000000000000", -1, "0x8000000000000000");
+    testBinary('i64', 'rem_u', "0x8ff00ff00ff00ff0", "0x100000001", "0x80000001");
 
-if (getBuildConfiguration().x64) {
+    // These should trap, but for now we match the i32 version.
+    testBinary('i64', 'div_s', 10, 0, 0);
+    testBinary('i64', 'div_s', "0x8000000000000000", -1, "0x8000000000000000");
+    testBinary('i64', 'div_u', 0, 0, 0);
+    testBinary('i64', 'rem_s', 10, 0, 0);
+    testBinary('i64', 'rem_u', 10, 0, 0);
+
     testBinary('i64', 'and', 42, 6, 2);
     testBinary('i64', 'or', 42, 6, 46);
     testBinary('i64', 'xor', 42, 2, 40);
     testBinary('i64', 'and', "0x8765432112345678", "0xffff0000ffff0000", "0x8765000012340000");
     testBinary('i64', 'or', "0x8765432112345678", "0xffff0000ffff0000", "0xffff4321ffff5678");
     testBinary('i64', 'xor', "0x8765432112345678", "0xffff0000ffff0000", "0x789a4321edcb5678");
     testBinary('i64', 'shl', 40, 2, 160);
     testBinary('i64', 'shr_s', -40, 2, -10);
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -1550,27 +1550,39 @@ LIRGenerator::visitAdd(MAdd* ins)
         if (ins->fallible())
             assignSnapshot(lir, Bailout_OverflowInvalidate);
 
         lowerForALU(lir, ins, lhs, rhs);
         MaybeSetRecoversInput(ins, lir);
         return;
     }
 
+    if (ins->specialization() == MIRType_Int64) {
+        MOZ_ASSERT(lhs->type() == MIRType_Int64);
+        ReorderCommutative(&lhs, &rhs, ins);
+        LAddI64* lir = new(alloc()) LAddI64;
+        lowerForALUInt64(lir, ins, lhs, rhs);
+        return;
+    }
+
     if (ins->specialization() == MIRType_Double) {
         MOZ_ASSERT(lhs->type() == MIRType_Double);
         ReorderCommutative(&lhs, &rhs, ins);
         lowerForFPU(new(alloc()) LMathD(JSOP_ADD), ins, lhs, rhs);
-    } else if (ins->specialization() == MIRType_Float32) {
+        return;
+    }
+
+    if (ins->specialization() == MIRType_Float32) {
         MOZ_ASSERT(lhs->type() == MIRType_Float32);
         ReorderCommutative(&lhs, &rhs, ins);
         lowerForFPU(new(alloc()) LMathF(JSOP_ADD), ins, lhs, rhs);
-    } else {
-        lowerBinaryV(JSOP_ADD, ins);
+        return;
     }
+
+    lowerBinaryV(JSOP_ADD, ins);
 }
 
 void
 LIRGenerator::visitSub(MSub* ins)
 {
     MDefinition* lhs = ins->lhs();
     MDefinition* rhs = ins->rhs();
 
@@ -1583,25 +1595,37 @@ LIRGenerator::visitSub(MSub* ins)
         if (ins->fallible())
             assignSnapshot(lir, Bailout_Overflow);
 
         lowerForALU(lir, ins, lhs, rhs);
         MaybeSetRecoversInput(ins, lir);
         return;
     }
 
+    if (ins->specialization() == MIRType_Int64) {
+        MOZ_ASSERT(lhs->type() == MIRType_Int64);
+        ReorderCommutative(&lhs, &rhs, ins);
+        LSubI64* lir = new(alloc()) LSubI64;
+        lowerForALUInt64(lir, ins, lhs, rhs);
+        return;
+    }
+
     if (ins->specialization() == MIRType_Double) {
         MOZ_ASSERT(lhs->type() == MIRType_Double);
         lowerForFPU(new(alloc()) LMathD(JSOP_SUB), ins, lhs, rhs);
-    } else if (ins->specialization() == MIRType_Float32) {
+        return;
+    }
+
+    if (ins->specialization() == MIRType_Float32) {
         MOZ_ASSERT(lhs->type() == MIRType_Float32);
         lowerForFPU(new(alloc()) LMathF(JSOP_SUB), ins, lhs, rhs);
-    } else {
-        lowerBinaryV(JSOP_SUB, ins);
+        return;
     }
+
+    lowerBinaryV(JSOP_SUB, ins);
 }
 
 void
 LIRGenerator::visitMul(MMul* ins)
 {
     MDefinition* lhs = ins->lhs();
     MDefinition* rhs = ins->rhs();
     MOZ_ASSERT(lhs->type() == rhs->type());
@@ -1611,81 +1635,120 @@ LIRGenerator::visitMul(MMul* ins)
         ReorderCommutative(&lhs, &rhs, ins);
 
         // If our RHS is a constant -1 and we don't have to worry about
         // overflow, we can optimize to an LNegI.
         if (!ins->fallible() && rhs->isConstant() && rhs->toConstant()->toInt32() == -1)
             defineReuseInput(new(alloc()) LNegI(useRegisterAtStart(lhs)), ins, 0);
         else
             lowerMulI(ins, lhs, rhs);
-    } else if (ins->specialization() == MIRType_Double) {
+        return;
+    }
+
+    if (ins->specialization() == MIRType_Int64) {
+        MOZ_ASSERT(lhs->type() == MIRType_Int64);
+        ReorderCommutative(&lhs, &rhs, ins);
+        LMulI64* lir = new(alloc()) LMulI64;
+        lowerForALUInt64(lir, ins, lhs, rhs);
+        return;
+    }
+
+    if (ins->specialization() == MIRType_Double) {
         MOZ_ASSERT(lhs->type() == MIRType_Double);
         ReorderCommutative(&lhs, &rhs, ins);
 
         // If our RHS is a constant -1.0, we can optimize to an LNegD.
         if (rhs->isConstant() && rhs->toConstant()->toDouble() == -1.0)
             defineReuseInput(new(alloc()) LNegD(useRegisterAtStart(lhs)), ins, 0);
         else
             lowerForFPU(new(alloc()) LMathD(JSOP_MUL), ins, lhs, rhs);
-    } else if (ins->specialization() == MIRType_Float32) {
+        return;
+    }
+
+    if (ins->specialization() == MIRType_Float32) {
         MOZ_ASSERT(lhs->type() == MIRType_Float32);
         ReorderCommutative(&lhs, &rhs, ins);
 
         // We apply the same optimizations as for doubles
         if (rhs->isConstant() && rhs->toConstant()->toFloat32() == -1.0f)
             defineReuseInput(new(alloc()) LNegF(useRegisterAtStart(lhs)), ins, 0);
         else
             lowerForFPU(new(alloc()) LMathF(JSOP_MUL), ins, lhs, rhs);
-    } else {
-        lowerBinaryV(JSOP_MUL, ins);
+        return;
     }
+
+    lowerBinaryV(JSOP_MUL, ins);
 }
 
 void
 LIRGenerator::visitDiv(MDiv* ins)
 {
     MDefinition* lhs = ins->lhs();
     MDefinition* rhs = ins->rhs();
     MOZ_ASSERT(lhs->type() == rhs->type());
 
     if (ins->specialization() == MIRType_Int32) {
         MOZ_ASSERT(lhs->type() == MIRType_Int32);
         lowerDivI(ins);
-    } else if (ins->specialization() == MIRType_Double) {
+        return;
+    }
+
+    if (ins->specialization() == MIRType_Int64) {
+        MOZ_ASSERT(lhs->type() == MIRType_Int64);
+        lowerDivI64(ins);
+        return;
+    }
+
+    if (ins->specialization() == MIRType_Double) {
         MOZ_ASSERT(lhs->type() == MIRType_Double);
         lowerForFPU(new(alloc()) LMathD(JSOP_DIV), ins, lhs, rhs);
-    } else if (ins->specialization() == MIRType_Float32) {
+        return;
+    }
+
+    if (ins->specialization() == MIRType_Float32) {
         MOZ_ASSERT(lhs->type() == MIRType_Float32);
         lowerForFPU(new(alloc()) LMathF(JSOP_DIV), ins, lhs, rhs);
-    } else {
-        lowerBinaryV(JSOP_DIV, ins);
+        return;
     }
+
+    lowerBinaryV(JSOP_DIV, ins);
 }
 
 void
 LIRGenerator::visitMod(MMod* ins)
 {
     MOZ_ASSERT(ins->lhs()->type() == ins->rhs()->type());
 
     if (ins->specialization() == MIRType_Int32) {
         MOZ_ASSERT(ins->type() == MIRType_Int32);
         MOZ_ASSERT(ins->lhs()->type() == MIRType_Int32);
         lowerModI(ins);
-    } else if (ins->specialization() == MIRType_Double) {
+        return;
+    }
+
+    if (ins->specialization() == MIRType_Int64) {
+        MOZ_ASSERT(ins->type() == MIRType_Int64);
+        MOZ_ASSERT(ins->lhs()->type() == MIRType_Int64);
+        lowerModI64(ins);
+        return;
+    }
+
+    if (ins->specialization() == MIRType_Double) {
         MOZ_ASSERT(ins->type() == MIRType_Double);
         MOZ_ASSERT(ins->lhs()->type() == MIRType_Double);
         MOZ_ASSERT(ins->rhs()->type() == MIRType_Double);
 
         // Note: useRegisterAtStart is safe here, the temp is not a FP register.
         LModD* lir = new(alloc()) LModD(useRegisterAtStart(ins->lhs()), useRegisterAtStart(ins->rhs()),
                                         tempFixed(CallTempReg0));
         defineReturn(lir, ins);
-    } else {
-        lowerBinaryV(JSOP_MOD, ins);
+        return;
     }
+
+    lowerBinaryV(JSOP_MOD, ins);
 }
 
 void
 LIRGenerator::lowerBinaryV(JSOp op, MBinaryInstruction* ins)
 {
     MDefinition* lhs = ins->getOperand(0);
     MDefinition* rhs = ins->getOperand(1);
 
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -2699,16 +2699,19 @@ MBinaryArithInstruction::constantDoubleR
 }
 
 MDefinition*
 MBinaryArithInstruction::foldsTo(TempAllocator& alloc)
 {
     if (specialization_ == MIRType_None)
         return this;
 
+    if (specialization_ == MIRType_Int64)
+        return this;
+
     MDefinition* lhs = getOperand(0);
     MDefinition* rhs = getOperand(1);
     if (MConstant* folded = EvaluateConstantOperands(alloc, this)) {
         if (isTruncated()) {
             if (!folded->block())
                 block()->insertBefore(this, folded);
             return MTruncateToInt32::New(alloc, folded);
         }
@@ -2911,16 +2914,19 @@ MAbs::trySpecializeFloat32(TempAllocator
 }
 
 MDefinition*
 MDiv::foldsTo(TempAllocator& alloc)
 {
     if (specialization_ == MIRType_None)
         return this;
 
+    if (specialization_ == MIRType_Int64)
+        return this;
+
     if (MDefinition* folded = EvaluateConstantOperands(alloc, this))
         return folded;
 
     if (MDefinition* folded = EvaluateExactReciprocal(alloc, this))
         return folded;
 
     return this;
 }
@@ -2973,16 +2979,19 @@ MDiv::fallible() const
 }
 
 MDefinition*
 MMod::foldsTo(TempAllocator& alloc)
 {
     if (specialization_ == MIRType_None)
         return this;
 
+    if (specialization_ == MIRType_Int64)
+        return this;
+
     if (MDefinition* folded = EvaluateConstantOperands(alloc, this))
         return folded;
 
     return this;
 }
 
 void
 MMod::analyzeEdgeCasesForward()
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -6723,23 +6723,23 @@ class MMod : public MBinaryArithInstruct
 
     MDefinition* foldsTo(TempAllocator& alloc) override;
 
     double getIdentity() override {
         MOZ_CRASH("not used");
     }
 
     bool canBeNegativeDividend() const {
-        MOZ_ASSERT(specialization_ == MIRType_Int32);
+        MOZ_ASSERT(specialization_ == MIRType_Int32 || specialization_ == MIRType_Int64);
         MOZ_ASSERT(!unsigned_);
         return canBeNegativeDividend_;
     }
 
     bool canBeDivideByZero() const {
-        MOZ_ASSERT(specialization_ == MIRType_Int32);
+        MOZ_ASSERT(specialization_ == MIRType_Int32 || specialization_ == MIRType_Int64);
         return canBeDivideByZero_;
     }
 
     bool canBePowerOfTwoDivisor() const {
         MOZ_ASSERT(specialization_ == MIRType_Int32);
         return canBePowerOfTwoDivisor_;
     }
 
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -740,16 +740,17 @@ class MacroAssembler : public MacroAssem
 
     inline void sub32(const Address& src, Register dest) PER_SHARED_ARCH;
     inline void sub32(Register src, Register dest) PER_SHARED_ARCH;
     inline void sub32(Imm32 imm, Register dest) PER_SHARED_ARCH;
 
     inline void subPtr(Register src, Register dest) PER_ARCH;
     inline void subPtr(Register src, const Address& dest) DEFINED_ON(mips_shared, arm, arm64, x86, x64);
     inline void subPtr(Imm32 imm, Register dest) PER_ARCH;
+    inline void subPtr(ImmWord imm, Register dest) DEFINED_ON(x64);
     inline void subPtr(const Address& addr, Register dest) DEFINED_ON(mips_shared, arm, arm64, x86, x64);
 
     inline void subDouble(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH;
 
     inline void mul32(Register src1, Register src2, Register dest, Label* onOver, Label* onZero) DEFINED_ON(arm64);
 
     inline void mul64(Imm64 imm, const Register64& dest) PER_ARCH;
 
--- a/js/src/jit/arm/Lowering-arm.cpp
+++ b/js/src/jit/arm/Lowering-arm.cpp
@@ -334,16 +334,28 @@ LIRGeneratorARM::lowerModI(MMod* mod)
                                             tempFixed(r0), tempFixed(r2), tempFixed(r3),
                                             temp(LDefinition::GENERAL));
     if (mod->fallible())
         assignSnapshot(lir, Bailout_DoubleOutput);
     defineFixed(lir, mod, LAllocation(AnyRegister(r1)));
 }
 
 void
+LIRGeneratorARM::lowerDivI64(MDiv* div)
+{
+    MOZ_CRASH("NYI");
+}
+
+void
+LIRGeneratorARM::lowerModI64(MMod* mod)
+{
+    MOZ_CRASH("NYI");
+}
+
+void
 LIRGeneratorARM::visitPowHalf(MPowHalf* ins)
 {
     MDefinition* input = ins->input();
     MOZ_ASSERT(input->type() == MIRType_Double);
     LPowHalfD* lir = new(alloc()) LPowHalfD(useRegisterAtStart(input));
     defineReuseInput(lir, ins, 0);
 }
 
--- a/js/src/jit/arm/Lowering-arm.h
+++ b/js/src/jit/arm/Lowering-arm.h
@@ -70,16 +70,18 @@ class LIRGeneratorARM : public LIRGenera
     }
 
     void lowerForBitAndAndBranch(LBitAndAndBranch* baab, MInstruction* mir,
                                  MDefinition* lhs, MDefinition* rhs);
     void lowerTruncateDToInt32(MTruncateToInt32* ins);
     void lowerTruncateFToInt32(MTruncateToInt32* ins);
     void lowerDivI(MDiv* div);
     void lowerModI(MMod* mod);
+    void lowerDivI64(MDiv* div);
+    void lowerModI64(MMod* mod);
     void lowerMulI(MMul* mul, MDefinition* lhs, MDefinition* rhs);
     void lowerUDiv(MDiv* div);
     void lowerUMod(MMod* mod);
     void visitPowHalf(MPowHalf* ins);
     void visitAsmJSNeg(MAsmJSNeg* ins);
 
     LTableSwitch* newLTableSwitch(const LAllocation& in, const LDefinition& inputCopy,
                                   MTableSwitch* ins);
--- a/js/src/jit/arm64/Lowering-arm64.cpp
+++ b/js/src/jit/arm64/Lowering-arm64.cpp
@@ -142,16 +142,28 @@ LIRGeneratorARM64::lowerMulI(MMul* mul, 
 
 void
 LIRGeneratorARM64::lowerModI(MMod* mod)
 {
     MOZ_CRASH("lowerModI");
 }
 
 void
+LIRGeneratorARM64::lowerDivI64(MDiv* div)
+{
+    MOZ_CRASH("NYI");
+}
+
+void
+LIRGeneratorARM64::lowerModI64(MMod* mod)
+{
+    MOZ_CRASH("NYI");
+}
+
+void
 LIRGeneratorARM64::visitPowHalf(MPowHalf* ins)
 {
     MOZ_CRASH("visitPowHalf");
 }
 
 LTableSwitch*
 LIRGeneratorARM64::newLTableSwitch(const LAllocation& in, const LDefinition& inputCopy,
                                        MTableSwitch* tableswitch)
--- a/js/src/jit/arm64/Lowering-arm64.h
+++ b/js/src/jit/arm64/Lowering-arm64.h
@@ -72,16 +72,18 @@ class LIRGeneratorARM64 : public LIRGene
     }
 
     void lowerForBitAndAndBranch(LBitAndAndBranch* baab, MInstruction* mir,
                                  MDefinition* lhs, MDefinition* rhs);
     void lowerTruncateDToInt32(MTruncateToInt32* ins);
     void lowerTruncateFToInt32(MTruncateToInt32* ins);
     void lowerDivI(MDiv* div);
     void lowerModI(MMod* mod);
+    void lowerDivI64(MDiv* div);
+    void lowerModI64(MMod* mod);
     void lowerMulI(MMul* mul, MDefinition* lhs, MDefinition* rhs);
     void lowerUDiv(MDiv* div);
     void lowerUMod(MMod* mod);
     void visitPowHalf(MPowHalf* ins);
     void visitAsmJSNeg(MAsmJSNeg* ins);
 
     LTableSwitchV* newLTableSwitchV(MTableSwitch* ins);
     LTableSwitch* newLTableSwitch(const LAllocation& in,
--- a/js/src/jit/mips-shared/Lowering-mips-shared.cpp
+++ b/js/src/jit/mips-shared/Lowering-mips-shared.cpp
@@ -184,16 +184,28 @@ LIRGeneratorMIPSShared::lowerModI(MMod* 
                            temp(LDefinition::GENERAL));
 
     if (mod->fallible())
         assignSnapshot(lir, Bailout_DoubleOutput);
     define(lir, mod);
 }
 
 void
+LIRGeneratorMIPSShared::lowerDivI64(MDiv* div)
+{
+    MOZ_CRASH("NYI");
+}
+
+void
+LIRGeneratorMIPSShared::lowerModI64(MMod* mod)
+{
+    MOZ_CRASH("NYI");
+}
+
+void
 LIRGeneratorMIPSShared::visitPowHalf(MPowHalf* ins)
 {
     MDefinition* input = ins->input();
     MOZ_ASSERT(input->type() == MIRType_Double);
     LPowHalfD* lir = new(alloc()) LPowHalfD(useRegisterAtStart(input));
     defineReuseInput(lir, ins, 0);
 }
 
--- a/js/src/jit/mips-shared/Lowering-mips-shared.h
+++ b/js/src/jit/mips-shared/Lowering-mips-shared.h
@@ -58,16 +58,18 @@ class LIRGeneratorMIPSShared : public LI
     {
         return lowerForFPU(ins, mir, lhs, rhs);
     }
 
     void lowerForBitAndAndBranch(LBitAndAndBranch* baab, MInstruction* mir,
                                  MDefinition* lhs, MDefinition* rhs);
     void lowerDivI(MDiv* div);
     void lowerModI(MMod* mod);
+    void lowerDivI64(MDiv* div);
+    void lowerModI64(MMod* mod);
     void lowerMulI(MMul* mul, MDefinition* lhs, MDefinition* rhs);
     void lowerUDiv(MDiv* div);
     void lowerUMod(MMod* mod);
     void visitPowHalf(MPowHalf* ins);
     void visitAsmJSNeg(MAsmJSNeg* ins);
 
     LTableSwitch* newLTableSwitch(const LAllocation& in, const LDefinition& inputCopy,
                                   MTableSwitch* ins);
--- a/js/src/jit/none/Lowering-none.h
+++ b/js/src/jit/none/Lowering-none.h
@@ -56,16 +56,18 @@ class LIRGeneratorNone : public LIRGener
     }
 
     void lowerConstantDouble(double, MInstruction*) { MOZ_CRASH(); }
     void lowerConstantFloat32(float, MInstruction*) { MOZ_CRASH(); }
     void lowerTruncateDToInt32(MTruncateToInt32*) { MOZ_CRASH(); }
     void lowerTruncateFToInt32(MTruncateToInt32*) { MOZ_CRASH(); }
     void lowerDivI(MDiv*) { MOZ_CRASH(); }
     void lowerModI(MMod*) { MOZ_CRASH(); }
+    void lowerDivI64(MDiv*) { MOZ_CRASH(); }
+    void lowerModI64(MMod*) { MOZ_CRASH(); }
     void lowerMulI(MMul*, MDefinition*, MDefinition*) { MOZ_CRASH(); }
     void lowerUDiv(MDiv*) { MOZ_CRASH(); }
     void lowerUMod(MMod*) { MOZ_CRASH(); }
     void visitBox(MBox* box) { MOZ_CRASH(); }
     void visitUnbox(MUnbox* unbox) { MOZ_CRASH(); }
     void visitReturn(MReturn* ret) { MOZ_CRASH(); }
     void visitPowHalf(MPowHalf*) { MOZ_CRASH(); }
     void visitAsmJSNeg(MAsmJSNeg*) { MOZ_CRASH(); }
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -3413,16 +3413,22 @@ class LAddI : public LBinaryMath<0>
         recoversInput_ = true;
     }
 
     MAdd* mir() const {
         return mir_->toAdd();
     }
 };
 
+class LAddI64 : public LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>
+{
+  public:
+    LIR_HEADER(AddI64)
+};
+
 // Subtracts two integers, returning an integer value.
 class LSubI : public LBinaryMath<0>
 {
     bool recoversInput_;
 
   public:
     LIR_HEADER(SubI)
 
@@ -3440,16 +3446,28 @@ class LSubI : public LBinaryMath<0>
     void setRecoversInput() {
         recoversInput_ = true;
     }
     MSub* mir() const {
         return mir_->toSub();
     }
 };
 
+class LSubI64 : public LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>
+{
+  public:
+    LIR_HEADER(SubI64)
+};
+
+class LMulI64 : public LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>
+{
+  public:
+    LIR_HEADER(MulI64)
+};
+
 // Performs an add, sub, mul, or div on two double values.
 class LMathD : public LBinaryMath<0>
 {
     JSOp jsop_;
 
   public:
     LIR_HEADER(MathD)
 
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -153,18 +153,21 @@
     _(MathFunctionD)                \
     _(MathFunctionF)                \
     _(NotI)                         \
     _(NotD)                         \
     _(NotF)                         \
     _(NotO)                         \
     _(NotV)                         \
     _(AddI)                         \
+    _(AddI64)                       \
     _(SubI)                         \
+    _(SubI64)                       \
     _(MulI)                         \
+    _(MulI64)                       \
     _(MathD)                        \
     _(MathF)                        \
     _(DivI)                         \
     _(DivPowTwoI)                   \
     _(ModI)                         \
     _(ModPowTwoI)                   \
     _(ModD)                         \
     _(BinaryV)                      \
--- a/js/src/jit/shared/Lowering-shared-inl.h
+++ b/js/src/jit/shared/Lowering-shared-inl.h
@@ -69,16 +69,45 @@ LIRGeneratorShared::defineFixed(LInstruc
 
     LDefinition def(type, LDefinition::FIXED);
     def.setOutput(output);
 
     define(lir, mir, def);
 }
 
 template <size_t Ops, size_t Temps> void
+LIRGeneratorShared::defineInt64Fixed(LInstructionHelper<INT64_PIECES, Ops, Temps>* lir, MDefinition* mir,
+                                     const LInt64Allocation& output)
+{
+    uint32_t vreg = getVirtualRegister();
+
+#if JS_BITS_PER_WORD == 64
+    LDefinition def(LDefinition::GENERAL, LDefinition::FIXED);
+    def.setOutput(output.value());
+    lir->setDef(0, def);
+    lir->getDef(0)->setVirtualRegister(vreg);
+#else
+    LDefinition def0(LDefinition::GENERAL, LDefinition::FIXED);
+    def0.setOutput(output.low());
+    lir->setDef(0, def0);
+    lir->getDef(0)->setVirtualRegister(vreg);
+
+    getVirtualRegister();
+    LDefinition def1(LDefinition::GENERAL, LDefinition::FIXED);
+    def1.setOutput(output.high());
+    lir->setDef(1, def1);
+    lir->getDef(1)->setVirtualRegister(vreg + 1);
+#endif
+
+    lir->setMir(mir);
+    mir->setVirtualRegister(vreg);
+    add(lir);
+}
+
+template <size_t Ops, size_t Temps> void
 LIRGeneratorShared::defineReuseInput(LInstructionHelper<1, Ops, Temps>* lir, MDefinition* mir, uint32_t operand)
 {
     // The input should be used at the start of the instruction, to avoid moves.
     MOZ_ASSERT(lir->getOperand(operand)->toUse()->usedAtStart());
 
     LDefinition::Type type = LDefinition::TypeFrom(mir->type());
 
     LDefinition def(type, LDefinition::MUST_REUSE_INPUT);
--- a/js/src/jit/shared/Lowering-shared.h
+++ b/js/src/jit/shared/Lowering-shared.h
@@ -142,16 +142,20 @@ class LIRGeneratorShared : public MDefin
     inline void defineBox(LInstructionHelper<BOX_PIECES, Ops, Temps>* lir, MDefinition* mir,
                           LDefinition::Policy policy = LDefinition::REGISTER);
 
     template <size_t Ops, size_t Temps>
     inline void defineInt64(LInstructionHelper<INT64_PIECES, Ops, Temps>* lir, MDefinition* mir,
                             LDefinition::Policy policy = LDefinition::REGISTER);
 
     template <size_t Ops, size_t Temps>
+    inline void defineInt64Fixed(LInstructionHelper<INT64_PIECES, Ops, Temps>* lir, MDefinition* mir,
+                                 const LInt64Allocation& output);
+
+    template <size_t Ops, size_t Temps>
     inline void defineSinCos(LInstructionHelper<2, Ops, Temps> *lir, MDefinition *mir,
                              LDefinition::Policy policy = LDefinition::REGISTER);
 
     inline void defineSharedStubReturn(LInstruction* lir, MDefinition* mir);
     inline void defineReturn(LInstruction* lir, MDefinition* mir);
 
     template <size_t X>
     inline void define(details::LInstructionFixedDefsTempsHelper<1, X>* lir, MDefinition* mir,
--- a/js/src/jit/x64/Assembler-x64.h
+++ b/js/src/jit/x64/Assembler-x64.h
@@ -612,20 +612,50 @@ class Assembler : public AssemblerX86Sha
           default:
             MOZ_CRASH("unexpected operand kind");
         }
     }
 
     void imulq(Register src, Register dest) {
         masm.imulq_rr(src.encoding(), dest.encoding());
     }
+    void imulq(const Operand& src, Register dest) {
+        switch (src.kind()) {
+          case Operand::REG:
+            masm.imulq_rr(src.reg(), dest.encoding());
+            break;
+          case Operand::MEM_REG_DISP:
+            masm.imulq_mr(src.disp(), src.base(), dest.encoding());
+            break;
+          case Operand::MEM_ADDRESS32:
+            MOZ_CRASH("NYI");
+            break;
+          default:
+            MOZ_CRASH("unexpected operand kind");
+        }
+    }
+
+    void cqo() {
+        masm.cqo();
+    }
+    void idivq(Register divisor) {
+        masm.idivq_r(divisor.encoding());
+    }
+    void udivq(Register divisor) {
+        masm.divq_r(divisor.encoding());
+    }
+
     void vcvtsi2sdq(Register src, FloatRegister dest) {
         masm.vcvtsi2sdq_rr(src.encoding(), dest.encoding());
     }
 
+    void negq(Register reg) {
+        masm.negq_r(reg.encoding());
+    }
+
     void mov(ImmWord word, Register dest) {
         // Use xor for setting registers to zero, as it is specially optimized
         // for this purpose on modern hardware. Note that it does clobber FLAGS
         // though. Use xorl instead of xorq since they are functionally
         // equivalent (32-bit instructions zero-extend their results to 64 bits)
         // and xorl has a smaller encoding.
         if (word.value == 0)
             xorl(dest, dest);
--- a/js/src/jit/x64/BaseAssembler-x64.h
+++ b/js/src/jit/x64/BaseAssembler-x64.h
@@ -288,16 +288,40 @@ class BaseAssemblerX64 : public BaseAsse
     }
 
     void imulq_rr(RegisterID src, RegisterID dst)
     {
         spew("imulq      %s, %s", GPReg64Name(src), GPReg64Name(dst));
         m_formatter.twoByteOp64(OP2_IMUL_GvEv, src, dst);
     }
 
+    void imulq_mr(int32_t offset, RegisterID base, RegisterID dst)
+    {
+        spew("imulq      " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(dst));
+        m_formatter.twoByteOp64(OP2_IMUL_GvEv, offset, base, dst);
+    }
+
+    void cqo()
+    {
+        spew("cqo        ");
+        m_formatter.oneByteOp64(OP_CDQ);
+    }
+
+    void idivq_r(RegisterID divisor)
+    {
+        spew("idivq      %s", GPReg64Name(divisor));
+        m_formatter.oneByteOp64(OP_GROUP3_Ev, divisor, GROUP3_OP_IDIV);
+    }
+
+    void divq_r(RegisterID divisor)
+    {
+        spew("divq       %s", GPReg64Name(divisor));
+        m_formatter.oneByteOp64(OP_GROUP3_Ev, divisor, GROUP3_OP_DIV);
+    }
+
     // Comparisons:
 
     void cmpq_rr(RegisterID rhs, RegisterID lhs)
     {
         spew("cmpq       %s, %s", GPReg64Name(rhs), GPReg64Name(lhs));
         m_formatter.oneByteOp64(OP_CMP_GvEv, rhs, lhs);
     }
 
--- a/js/src/jit/x64/CodeGenerator-x64.cpp
+++ b/js/src/jit/x64/CodeGenerator-x64.cpp
@@ -1,16 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/x64/CodeGenerator-x64.h"
 
+#include "mozilla/MathAlgorithms.h"
+
 #include "jit/IonCaches.h"
 #include "jit/MIR.h"
 
 #include "jsscriptinlines.h"
 
 #include "jit/MacroAssembler-inl.h"
 #include "jit/shared/CodeGenerator-shared-inl.h"
 
@@ -317,16 +319,170 @@ CodeGeneratorX64::visitShiftI64(LShiftI6
             break;
           default:
             MOZ_CRASH("Unexpected shift op");
         }
     }
 }
 
 void
+CodeGeneratorX64::visitAddI64(LAddI64* lir)
+{
+    Register lhs = ToRegister(lir->getOperand(0));
+    const LAllocation* rhs = lir->getOperand(1);
+
+    MOZ_ASSERT(ToRegister(lir->getDef(0)) == lhs);
+
+    if (rhs->isConstant())
+        masm.addPtr(ImmWord(ToInt64(rhs)), lhs);
+    else
+        masm.addq(ToOperand(rhs), lhs);
+}
+
+void
+CodeGeneratorX64::visitSubI64(LSubI64* lir)
+{
+    Register lhs = ToRegister(lir->getOperand(0));
+    const LAllocation* rhs = lir->getOperand(1);
+
+    MOZ_ASSERT(ToRegister(lir->getDef(0)) == lhs);
+
+    if (rhs->isConstant())
+        masm.subPtr(ImmWord(ToInt64(rhs)), lhs);
+    else
+        masm.subq(ToOperand(rhs), lhs);
+}
+
+void
+CodeGeneratorX64::visitMulI64(LMulI64* lir)
+{
+    Register lhs = ToRegister(lir->getOperand(0));
+    const LAllocation* rhs = lir->getOperand(1);
+
+    MOZ_ASSERT(ToRegister(lir->getDef(0)) == lhs);
+
+    if (rhs->isConstant()) {
+        int64_t constant = ToInt64(rhs);
+        switch (constant) {
+          case -1:
+            masm.negq(lhs);
+            return;
+          case 0:
+            masm.xorl(lhs, lhs);
+            return;
+          case 1:
+            // nop
+            return;
+          case 2:
+            masm.addq(lhs, lhs);
+            return;
+          default:
+            if (constant > 0) {
+                // Use shift if constant is power of 2.
+                int32_t shift = mozilla::FloorLog2(constant);
+                if (int64_t(1 << shift) == constant) {
+                    masm.shlq(Imm32(shift), lhs);
+                    return;
+                }
+            }
+            masm.mul64(Imm64(constant), Register64(lhs));
+        }
+    } else {
+        masm.imulq(ToOperand(rhs), lhs);
+    }
+}
+
+void
+CodeGeneratorX64::visitDivOrModI64(LDivOrModI64* lir)
+{
+    Register lhs = ToRegister(lir->lhs());
+    Register rhs = ToRegister(lir->rhs());
+    Register output = ToRegister(lir->output());
+
+    MOZ_ASSERT_IF(lhs != rhs, rhs != rax);
+    MOZ_ASSERT(rhs != rdx);
+    MOZ_ASSERT_IF(output == rax, ToRegister(lir->remainder()) == rdx);
+    MOZ_ASSERT_IF(output == rdx, ToRegister(lir->remainder()) == rax);
+
+    Label done;
+
+    // Put the lhs in rax.
+    if (lhs != rax)
+        masm.mov(lhs, rax);
+
+    // Handle divide by zero. For now match asm.js and return 0, but
+    // eventually this should trap.
+    if (lir->canBeDivideByZero()) {
+        Label nonZero;
+        masm.branchTestPtr(Assembler::NonZero, rhs, rhs, &nonZero);
+        masm.xorl(output, output);
+        masm.jump(&done);
+        masm.bind(&nonZero);
+    }
+
+    // Handle an integer overflow exception from INT64_MIN / -1. Eventually
+    // signed integer division should trap, instead of returning the
+    // LHS (INT64_MIN).
+    if (lir->canBeNegativeOverflow()) {
+        Label notmin;
+        masm.branchPtr(Assembler::NotEqual, lhs, ImmWord(INT64_MIN), &notmin);
+        masm.branchPtr(Assembler::NotEqual, rhs, ImmWord(-1), &notmin);
+        if (lir->mir()->isMod()) {
+            masm.xorl(output, output);
+        } else {
+            if (lhs != output)
+                masm.mov(lhs, output);
+        }
+        masm.jump(&done);
+        masm.bind(&notmin);
+    }
+
+    // Sign extend the lhs into rdx to make rdx:rax.
+    masm.cqo();
+    masm.idivq(rhs);
+
+    masm.bind(&done);
+}
+
+void
+CodeGeneratorX64::visitUDivOrMod64(LUDivOrMod64* lir)
+{
+    Register lhs = ToRegister(lir->lhs());
+    Register rhs = ToRegister(lir->rhs());
+    Register output = ToRegister(lir->output());
+
+    MOZ_ASSERT_IF(lhs != rhs, rhs != rax);
+    MOZ_ASSERT(rhs != rdx);
+    MOZ_ASSERT_IF(output == rax, ToRegister(lir->remainder()) == rdx);
+    MOZ_ASSERT_IF(output == rdx, ToRegister(lir->remainder()) == rax);
+
+    // Put the lhs in rax.
+    if (lhs != rax)
+        masm.mov(lhs, rax);
+
+    Label done;
+
+    // Prevent divide by zero. For now match asm.js and return 0, but
+    // eventually this should trap.
+    if (lir->canBeDivideByZero()) {
+        Label nonZero;
+        masm.branchTestPtr(Assembler::NonZero, rhs, rhs, &nonZero);
+        masm.xorl(output, output);
+        masm.jump(&done);
+        masm.bind(&nonZero);
+    }
+
+    // Zero extend the lhs into rdx to make (rdx:rax).
+    masm.xorl(rdx, rdx);
+    masm.udivq(rhs);
+
+    masm.bind(&done);
+}
+
+void
 CodeGeneratorX64::visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble* lir)
 {
     masm.convertUInt32ToDouble(ToRegister(lir->input()), ToFloatRegister(lir->output()));
 }
 
 void
 CodeGeneratorX64::visitAsmJSUInt32ToFloat32(LAsmJSUInt32ToFloat32* lir)
 {
--- a/js/src/jit/x64/CodeGenerator-x64.h
+++ b/js/src/jit/x64/CodeGenerator-x64.h
@@ -41,16 +41,21 @@ class CodeGeneratorX64 : public CodeGene
     void visitCompareB(LCompareB* lir);
     void visitCompareBAndBranch(LCompareBAndBranch* lir);
     void visitCompareBitwise(LCompareBitwise* lir);
     void visitCompareBitwiseAndBranch(LCompareBitwiseAndBranch* lir);
     void visitCompare64(LCompare64* lir);
     void visitCompare64AndBranch(LCompare64AndBranch* lir);
     void visitBitOpI64(LBitOpI64* lir);
     void visitShiftI64(LShiftI64* lir);
+    void visitAddI64(LAddI64* lir);
+    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 visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins);
     void visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins);
     void visitAsmJSCall(LAsmJSCall* ins);
     void visitAsmJSLoadHeap(LAsmJSLoadHeap* ins);
     void visitAsmJSStoreHeap(LAsmJSStoreHeap* ins);
     void visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins);
--- a/js/src/jit/x64/LIR-x64.h
+++ b/js/src/jit/x64/LIR-x64.h
@@ -94,12 +94,77 @@ class LAsmJSLoadFuncPtr : public LInstru
     const LAllocation* index() {
         return getOperand(0);
     }
     const LDefinition* temp() {
         return getTemp(0);
     }
 };
 
+class LDivOrModI64 : public LBinaryMath<1>
+{
+  public:
+    LIR_HEADER(DivOrModI64)
+
+    LDivOrModI64(const LAllocation& lhs, const LAllocation& rhs, const LDefinition& temp) {
+        setOperand(0, lhs);
+        setOperand(1, rhs);
+        setTemp(0, temp);
+    }
+
+    const LDefinition* remainder() {
+        return getTemp(0);
+    }
+
+    MBinaryArithInstruction* mir() const {
+        MOZ_ASSERT(mir_->isDiv() || mir_->isMod());
+        return static_cast<MBinaryArithInstruction*>(mir_);
+    }
+    bool canBeDivideByZero() const {
+        if (mir_->isMod())
+            return mir_->toMod()->canBeDivideByZero();
+        return mir_->toDiv()->canBeDivideByZero();
+    }
+    bool canBeNegativeOverflow() const {
+        if (mir_->isMod())
+            return mir_->toMod()->canBeNegativeDividend();
+        return mir_->toDiv()->canBeNegativeOverflow();
+    }
+};
+
+// This class performs a simple x86 'div', yielding either a quotient or
+// remainder depending on whether this instruction is defined to output
+// rax (quotient) or rdx (remainder).
+class LUDivOrMod64 : public LBinaryMath<1>
+{
+  public:
+    LIR_HEADER(UDivOrMod64);
+
+    LUDivOrMod64(const LAllocation& lhs, const LAllocation& rhs, const LDefinition& temp) {
+        setOperand(0, lhs);
+        setOperand(1, rhs);
+        setTemp(0, temp);
+    }
+
+    const LDefinition* remainder() {
+        return getTemp(0);
+    }
+
+    const char* extraName() const {
+        return mir()->isTruncated() ? "Truncated" : nullptr;
+    }
+
+    MBinaryArithInstruction* mir() const {
+        MOZ_ASSERT(mir_->isDiv() || mir_->isMod());
+        return static_cast<MBinaryArithInstruction*>(mir_);
+    }
+
+    bool canBeDivideByZero() const {
+        if (mir_->isMod())
+            return mir_->toMod()->canBeDivideByZero();
+        return mir_->toDiv()->canBeDivideByZero();
+    }
+};
+
 } // 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
@@ -6,14 +6,16 @@
 
 #ifndef jit_x64_LOpcodes_x64_h
 #define jit_x64_LOpcodes_x64_h
 
 #include "jit/shared/LOpcodes-shared.h"
 
 #define LIR_CPU_OPCODE_LIST(_)      \
     _(DivOrModConstantI)            \
+    _(DivOrModI64)                  \
+    _(UDivOrMod64)                  \
     _(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
@@ -336,8 +336,52 @@ LIRGeneratorX64::visitStoreTypedArrayEle
 void
 LIRGeneratorX64::visitRandom(MRandom* ins)
 {
     LRandom *lir = new(alloc()) LRandom(temp(),
                                         temp(),
                                         temp());
     defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
 }
+
+void
+LIRGeneratorX64::lowerDivI64(MDiv* div)
+{
+    if (div->isUnsigned()) {
+        lowerUDiv64(div);
+        return;
+    }
+
+    LDivOrModI64* lir = new(alloc()) LDivOrModI64(useRegister(div->lhs()), useRegister(div->rhs()),
+                                                  tempFixed(rdx));
+    defineInt64Fixed(lir, div, LInt64Allocation(LAllocation(AnyRegister(rax))));
+}
+
+void
+LIRGeneratorX64::lowerModI64(MMod* mod)
+{
+    if (mod->isUnsigned()) {
+        lowerUMod64(mod);
+        return;
+    }
+
+    LDivOrModI64* lir = new(alloc()) LDivOrModI64(useRegister(mod->lhs()), useRegister(mod->rhs()),
+                                                  tempFixed(rax));
+    defineInt64Fixed(lir, mod, LInt64Allocation(LAllocation(AnyRegister(rdx))));
+}
+
+void
+LIRGeneratorX64::lowerUDiv64(MDiv* div)
+{
+    LUDivOrMod64* lir = new(alloc()) LUDivOrMod64(useRegister(div->lhs()),
+                                                  useRegister(div->rhs()),
+                                                  tempFixed(rdx));
+    defineInt64Fixed(lir, div, LInt64Allocation(LAllocation(AnyRegister(rax))));
+}
+
+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))));
+}
--- a/js/src/jit/x64/Lowering-x64.h
+++ b/js/src/jit/x64/Lowering-x64.h
@@ -31,16 +31,21 @@ class LIRGeneratorX64 : public LIRGenera
     LAllocation useByteOpRegister(MDefinition* mir);
     LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition* mir);
     LDefinition tempByteOpRegister();
 
     LDefinition tempToUnbox();
 
     bool needTempForPostBarrier() { return false; }
 
+    void lowerDivI64(MDiv* div);
+    void lowerModI64(MMod* mod);
+    void lowerUDiv64(MDiv* div);
+    void lowerUMod64(MMod* mod);
+
   public:
     void visitBox(MBox* box);
     void visitUnbox(MUnbox* unbox);
     void visitReturn(MReturn* ret);
     void visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins);
     void visitAtomicExchangeTypedArrayElement(MAtomicExchangeTypedArrayElement* ins);
     void visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins);
     void visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble* ins);
--- a/js/src/jit/x64/MacroAssembler-x64-inl.h
+++ b/js/src/jit/x64/MacroAssembler-x64-inl.h
@@ -185,16 +185,29 @@ MacroAssembler::subPtr(Register src, con
 
 void
 MacroAssembler::subPtr(Imm32 imm, Register dest)
 {
     subq(imm, dest);
 }
 
 void
+MacroAssembler::subPtr(ImmWord imm, Register dest)
+{
+    ScratchRegisterScope scratch(*this);
+    MOZ_ASSERT(dest != scratch);
+    if ((intptr_t)imm.value <= INT32_MAX && (intptr_t)imm.value >= INT32_MIN) {
+        subq(Imm32((int32_t)imm.value), dest);
+    } else {
+        mov(imm, scratch);
+        subq(scratch, dest);
+    }
+}
+
+void
 MacroAssembler::subPtr(const Address& addr, Register dest)
 {
     subq(Operand(addr), dest);
 }
 
 void
 MacroAssembler::mul64(Imm64 imm, const Register64& dest)
 {
--- a/js/src/jit/x86/Lowering-x86.cpp
+++ b/js/src/jit/x86/Lowering-x86.cpp
@@ -416,16 +416,28 @@ LIRGeneratorX86::visitAsmJSAtomicBinopHe
 
 void
 LIRGeneratorX86::visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr* ins)
 {
     define(new(alloc()) LAsmJSLoadFuncPtr(useRegisterAtStart(ins->index())), ins);
 }
 
 void
+LIRGeneratorX86::lowerDivI64(MDiv* div)
+{
+    MOZ_CRASH("NYI");
+}
+
+void
+LIRGeneratorX86::lowerModI64(MMod* mod)
+{
+    MOZ_CRASH("NYI");
+}
+
+void
 LIRGeneratorX86::visitSubstr(MSubstr* ins)
 {
     // Due to lack of registers on x86, we reuse the string register as
     // temporary. As a result we only need two temporary registers and take a
     // bugos temporary as fifth argument.
     LSubstr* lir = new (alloc()) LSubstr(useRegister(ins->string()),
                                          useRegister(ins->begin()),
                                          useRegister(ins->length()),
--- a/js/src/jit/x86/Lowering-x86.h
+++ b/js/src/jit/x86/Lowering-x86.h
@@ -37,16 +37,19 @@ class LIRGeneratorX86 : public LIRGenera
         return LDefinition::BogusTemp();
     }
 
     bool needTempForPostBarrier() { return true; }
 
     void lowerUntypedPhiInput(MPhi* phi, uint32_t inputPosition, LBlock* block, size_t lirIndex);
     void defineUntypedPhi(MPhi* phi, size_t lirIndex);
 
+    void lowerDivI64(MDiv* div);
+    void lowerModI64(MMod* mod);
+
   public:
     void visitBox(MBox* box);
     void visitUnbox(MUnbox* unbox);
     void visitReturn(MReturn* ret);
     void visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins);
     void visitAtomicExchangeTypedArrayElement(MAtomicExchangeTypedArrayElement* ins);
     void visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins);
     void visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble* ins);