Bug 1250165 - Implement wasm i64 bitwise ops. r=luke
authorJan de Mooij <jdemooij@mozilla.com>
Thu, 25 Feb 2016 14:59:32 +0100
changeset 323813 6f7d0257dc2e17e5a6142e76e68b9ec0b7e9c060
parent 323812 c167859aa1f5a4d8c3ce76c5d841eabdbce59241
child 323814 32a68db1df317ba121c8ceae452273082883da07
push id1128
push userjlund@mozilla.com
push dateWed, 01 Jun 2016 01:31:59 +0000
treeherdermozilla-release@fe0d30de989d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1250165
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 1250165 - Implement wasm i64 bitwise ops. r=luke
js/src/asmjs/Wasm.cpp
js/src/asmjs/WasmIonCompile.cpp
js/src/jit-test/tests/wasm/basic-integer.js
js/src/jit/AlignmentMaskAnalysis.cpp
js/src/jit/Lowering.cpp
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jit/MacroAssembler.h
js/src/jit/RangeAnalysis.cpp
js/src/jit/arm/Lowering-arm.cpp
js/src/jit/arm/Lowering-arm.h
js/src/jit/arm/MacroAssembler-arm-inl.h
js/src/jit/arm64/Lowering-arm64.cpp
js/src/jit/arm64/Lowering-arm64.h
js/src/jit/arm64/MacroAssembler-arm64-inl.h
js/src/jit/mips-shared/Lowering-mips-shared.cpp
js/src/jit/mips-shared/Lowering-mips-shared.h
js/src/jit/mips32/MacroAssembler-mips32-inl.h
js/src/jit/mips64/MacroAssembler-mips64-inl.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/MacroAssembler-x64-inl.h
js/src/jit/x86-shared/Lowering-x86-shared.cpp
js/src/jit/x86-shared/Lowering-x86-shared.h
js/src/jit/x86/MacroAssembler-x86-inl.h
--- a/js/src/asmjs/Wasm.cpp
+++ b/js/src/asmjs/Wasm.cpp
@@ -473,24 +473,24 @@ 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 f.fail("NYI: i64") &&
-               DecodeBinaryOperator(f, expected, ExprType::I64);
+        return DecodeBinaryOperator(f, expected, ExprType::I64);
       case Expr::F32Add:
       case Expr::F32Sub:
       case Expr::F32Mul:
       case Expr::F32Div:
       case Expr::F32Min:
       case Expr::F32Max:
         return DecodeBinaryOperator(f, expected, ExprType::F32);
       case Expr::F32CopySign:
--- a/js/src/asmjs/WasmIonCompile.cpp
+++ b/js/src/asmjs/WasmIonCompile.cpp
@@ -440,21 +440,21 @@ class FunctionCompiler
         if (inDeadCode())
             return nullptr;
         MMod* ins = MMod::NewAsmJS(alloc(), lhs, rhs, type, unsignd);
         curBlock_->add(ins);
         return ins;
     }
 
     template <class T>
-    MDefinition* bitwise(MDefinition* lhs, MDefinition* rhs)
+    MDefinition* bitwise(MDefinition* lhs, MDefinition* rhs, MIRType type)
     {
         if (inDeadCode())
             return nullptr;
-        T* ins = T::NewAsmJS(alloc(), lhs, rhs);
+        T* ins = T::NewAsmJS(alloc(), lhs, rhs, type);
         curBlock_->add(ins);
         return ins;
     }
 
     template <class T>
     MDefinition* bitwise(MDefinition* op)
     {
         if (inDeadCode())
@@ -781,17 +781,17 @@ class FunctionCompiler
             return true;
         }
 
         MAsmJSLoadFuncPtr* ptrFun;
         if (mg().isAsmJS()) {
             MOZ_ASSERT(IsPowerOfTwo(length));
             MConstant* mask = MConstant::New(alloc(), Int32Value(length - 1));
             curBlock_->add(mask);
-            MBitAnd* maskedIndex = MBitAnd::NewAsmJS(alloc(), index, mask);
+            MBitAnd* maskedIndex = MBitAnd::NewAsmJS(alloc(), index, mask, MIRType_Int32);
             curBlock_->add(maskedIndex);
             ptrFun = MAsmJSLoadFuncPtr::New(alloc(), maskedIndex, globalDataOffset);
             curBlock_->add(ptrFun);
         } else {
             // For wasm code, as a space optimization, the ModuleGenerator does not allocate a
             // table for signatures which do not contain any indirectly-callable functions.
             // However, these signatures may still be called (it is not a validation error)
             // so we instead have a flag alwaysThrow which throws an exception instead of loading
@@ -2323,31 +2323,31 @@ EmitComparison(FunctionCompiler& f, Expr
     }
 
     *def = f.compare(lhs, rhs, compareOp, compareType);
     return true;
 }
 
 template<class T>
 static bool
-EmitBitwise(FunctionCompiler& f, MDefinition** def)
+EmitBitwise(FunctionCompiler& f, ExprType type, MDefinition** def)
 {
     MDefinition* lhs;
-    if (!EmitExpr(f, ExprType::I32, &lhs))
+    if (!EmitExpr(f, type, &lhs))
         return false;
     MDefinition* rhs;
-    if (!EmitExpr(f, ExprType::I32, &rhs))
+    if (!EmitExpr(f, type, &rhs))
         return false;
-    *def = f.bitwise<T>(lhs, rhs);
+    MIRType mirType = ToMIRType(type);
+    *def = f.bitwise<T>(lhs, rhs, mirType);
     return true;
 }
 
-template<>
-bool
-EmitBitwise<MBitNot>(FunctionCompiler& f, MDefinition** def)
+static bool
+EmitBitwiseNot(FunctionCompiler& f, MDefinition** def)
 {
     MDefinition* in;
     if (!EmitExpr(f, ExprType::I32, &in))
         return false;
     *def = f.bitwise<MBitNot>(in);
     return true;
 }
 
@@ -2815,29 +2815,29 @@ EmitExpr(FunctionCompiler& f, ExprType t
         return EmitUnary<MTruncateToInt32>(f, ExprType::F64, def);
       case Expr::I32Clz:
         return EmitUnary<MClz>(f, ExprType::I32, def);
       case Expr::I32Abs:
         return EmitUnaryMir<MAbs>(f, ExprType::I32, def);
       case Expr::I32Neg:
         return EmitUnaryMir<MAsmJSNeg>(f, ExprType::I32, def);
       case Expr::I32Or:
-        return EmitBitwise<MBitOr>(f, def);
+        return EmitBitwise<MBitOr>(f, ExprType::I32, def);
       case Expr::I32And:
-        return EmitBitwise<MBitAnd>(f, def);
+        return EmitBitwise<MBitAnd>(f, ExprType::I32, def);
       case Expr::I32Xor:
-        return EmitBitwise<MBitXor>(f, def);
+        return EmitBitwise<MBitXor>(f, ExprType::I32, def);
       case Expr::I32Shl:
-        return EmitBitwise<MLsh>(f, def);
+        return EmitBitwise<MLsh>(f, ExprType::I32, def);
       case Expr::I32ShrS:
-        return EmitBitwise<MRsh>(f, def);
+        return EmitBitwise<MRsh>(f, ExprType::I32, def);
       case Expr::I32ShrU:
-        return EmitBitwise<MUrsh>(f, def);
+        return EmitBitwise<MUrsh>(f, ExprType::I32, def);
       case Expr::I32BitNot:
-        return EmitBitwise<MBitNot>(f, def);
+        return EmitBitwiseNot(f, def);
       case Expr::I32LoadMem8S:
         return EmitLoad(f, Scalar::Int8, def);
       case Expr::I32LoadMem8U:
         return EmitLoad(f, Scalar::Uint8, def);
       case Expr::I32LoadMem16S:
         return EmitLoad(f, Scalar::Int16, def);
       case Expr::I32LoadMem16U:
         return EmitLoad(f, Scalar::Uint16, def);
@@ -2890,16 +2890,28 @@ EmitExpr(FunctionCompiler& f, ExprType t
         return EmitAtomicsLoad(f, def);
       case Expr::I32AtomicsStore:
         return EmitAtomicsStore(f, def);
       case Expr::I32AtomicsBinOp:
         return EmitAtomicsBinOp(f, def);
       // I64
       case Expr::I64Const:
         return EmitLiteral(f, ExprType::I64, 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);
+      case Expr::I64ShrS:
+        return EmitBitwise<MRsh>(f, ExprType::I64, def);
+      case Expr::I64ShrU:
+        return EmitBitwise<MUrsh>(f, ExprType::I64, 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:
@@ -3066,22 +3078,16 @@ EmitExpr(FunctionCompiler& f, ExprType t
       case Expr::I64Popcnt:
       case Expr::I64Add:
       case Expr::I64Sub:
       case Expr::I64Mul:
       case Expr::I64DivS:
       case Expr::I64DivU:
       case Expr::I64RemS:
       case Expr::I64RemU:
-      case Expr::I64Or:
-      case Expr::I64And:
-      case Expr::I64Xor:
-      case Expr::I64Shl:
-      case Expr::I64ShrU:
-      case Expr::I64ShrS:
         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
@@ -4,21 +4,29 @@ assertEq(wasmEvalText('(module (func (re
 assertEq(wasmEvalText('(module (func (result i32) (i32.const -2147483648)) (export "" 0))')(), -2147483648);
 assertEq(wasmEvalText('(module (func (result i32) (i32.const 4294967295)) (export "" 0))')(), -1);
 
 function testUnary(type, opcode, op, expect) {
   assertEq(wasmEvalText('(module (func (param ' + type + ') (result ' + type + ') (' + type + '.' + opcode + ' (get_local 0))) (export "" 0))')(op), expect);
 }
 
 function testBinary(type, opcode, lhs, rhs, expect) {
-  assertEq(wasmEvalText('(module (func (param ' + type + ') (param ' + type + ') (result ' + type + ') (' + type + '.' + opcode + ' (get_local 0) (get_local 1))) (export "" 0))')(lhs, rhs), expect);
+  if (type === 'i64') {
+    // i64 cannot be imported/exported, so we use a wrapper function.
+    assertEq(wasmEvalText(`(module
+                            (func (param i64) (param i64) (result i64) (i64.${opcode} (get_local 0) (get_local 1)))
+                            (func (result i32) (i64.eq (call 0 (i64.const ${lhs}) (i64.const ${rhs})) (i64.const ${expect})))
+                            (export "" 1))`)(), 1);
+  } else {
+    assertEq(wasmEvalText('(module (func (param ' + type + ') (param ' + type + ') (result ' + type + ') (' + type + '.' + opcode + ' (get_local 0) (get_local 1))) (export "" 0))')(lhs, rhs), expect);
+  }
 }
 
 function testComparison(type, opcode, lhs, rhs, expect) {
-  if (type == 'i64') {
+  if (type === 'i64') {
     // i64 cannot be imported/exported, so we use a wrapper function.
     assertEq(wasmEvalText(`(module
                             (func (param i64) (param i64) (result i32) (i64.${opcode} (get_local 0) (get_local 1)))
                             (func (result i32) (call 0 (i64.const ${lhs}) (i64.const ${rhs})))
                             (export "" 1))`)(), expect);
     // Also test if_else, for the compare-and-branch path.
     assertEq(wasmEvalText(`(module
                             (func (param i64) (param i64) (result i32)
@@ -67,24 +75,33 @@ testComparison('i32', 'ge_u', 40, 40, 1)
 
 //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
-//testBinary('i64', 'and', 42, 6, 2); // TODO: NYI
-//testBinary('i64', 'or', 42, 6, 46); // TODO: NYI
-//testBinary('i64', 'xor', 42, 2, 40); // TODO: NYI
-//testBinary('i64', 'shl', 40, 2, 160); // TODO: NYI
-//testBinary('i64', 'shr_s', -40, 2, -10); // TODO: NYI
-//testBinary('i64', 'shr_u', -40, 2, 1073741814); // TODO: NYI
 
 if (getBuildConfiguration().x64) {
+    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);
+    testBinary('i64', 'shr_u', -40, 2, "0x3ffffffffffffff6");
+    testBinary('i64', 'shl', 0xff00ff, 28, "0xff00ff0000000");
+    testBinary('i64', 'shl', 1, 63, "0x8000000000000000");
+    testBinary('i64', 'shl', 1, 64, 1);
+    testBinary('i64', 'shr_s', "0xff00ff0000000", 28, 0xff00ff);
+    testBinary('i64', 'shr_u', "0x8ffff00ff0000000", 56, 0x8f);
+
     testComparison('i64', 'eq', 40, 40, 1);
     testComparison('i64', 'ne', 40, 40, 0);
     testComparison('i64', 'lt_s', 40, 40, 0);
     testComparison('i64', 'lt_u', 40, 40, 0);
     testComparison('i64', 'le_s', 40, 40, 1);
     testComparison('i64', 'le_u', 40, 40, 1);
     testComparison('i64', 'gt_s', 40, 40, 0);
     testComparison('i64', 'gt_u', 40, 40, 0);
--- a/js/src/jit/AlignmentMaskAnalysis.cpp
+++ b/js/src/jit/AlignmentMaskAnalysis.cpp
@@ -60,17 +60,17 @@ AnalyzeAsmHeapAddress(MDefinition* ptr, 
         return;
 
     uint32_t i = op1->toConstant()->toInt32();
     uint32_t m = rhs->toConstant()->toInt32();
     if (!IsAlignmentMask(m) || (i & m) != i)
         return;
 
     // The pattern was matched! Produce the replacement expression.
-    MInstruction* and_ = MBitAnd::NewAsmJS(graph.alloc(), op0, rhs);
+    MInstruction* and_ = MBitAnd::NewAsmJS(graph.alloc(), op0, rhs, MIRType_Int32);
     ptr->block()->insertBefore(ptr->toBitAnd(), and_);
     MInstruction* add = MAdd::NewAsmJS(graph.alloc(), and_, op1, MIRType_Int32);
     ptr->block()->insertBefore(ptr->toBitAnd(), add);
     ptr->replaceAllUsesWith(add);
     ptr->block()->discard(ptr->toBitAnd());
 }
 
 bool
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -1116,22 +1116,30 @@ LIRGenerator::visitCompare(MCompare* com
 }
 
 void
 LIRGenerator::lowerBitOp(JSOp op, MInstruction* ins)
 {
     MDefinition* lhs = ins->getOperand(0);
     MDefinition* rhs = ins->getOperand(1);
 
-    if (lhs->type() == MIRType_Int32 && rhs->type() == MIRType_Int32) {
+    if (lhs->type() == MIRType_Int32) {
+        MOZ_ASSERT(rhs->type() == MIRType_Int32);
         ReorderCommutative(&lhs, &rhs, ins);
         lowerForALU(new(alloc()) LBitOpI(op), ins, lhs, rhs);
         return;
     }
 
+    if (lhs->type() == MIRType_Int64) {
+        MOZ_ASSERT(rhs->type() == MIRType_Int64);
+        ReorderCommutative(&lhs, &rhs, ins);
+        lowerForALUInt64(new(alloc()) LBitOpI64(op), ins, lhs, rhs);
+        return;
+    }
+
     LBitOpV* lir = new(alloc()) LBitOpV(op, useBoxAtStart(lhs), useBoxAtStart(rhs));
     defineReturn(lir, ins);
     assignSafepoint(lir, ins);
 }
 
 void
 LIRGenerator::visitTypeOf(MTypeOf* ins)
 {
@@ -1215,32 +1223,40 @@ LIRGenerator::visitBitXor(MBitXor* ins)
 }
 
 void
 LIRGenerator::lowerShiftOp(JSOp op, MShiftInstruction* ins)
 {
     MDefinition* lhs = ins->getOperand(0);
     MDefinition* rhs = ins->getOperand(1);
 
-    if (lhs->type() == MIRType_Int32 && rhs->type() == MIRType_Int32) {
+    if (lhs->type() == MIRType_Int32) {
+        MOZ_ASSERT(rhs->type() == MIRType_Int32);
+
         if (ins->type() == MIRType_Double) {
             MOZ_ASSERT(op == JSOP_URSH);
             lowerUrshD(ins->toUrsh());
             return;
         }
 
         LShiftI* lir = new(alloc()) LShiftI(op);
         if (op == JSOP_URSH) {
             if (ins->toUrsh()->fallible())
                 assignSnapshot(lir, Bailout_OverflowInvalidate);
         }
         lowerForShift(lir, ins, lhs, rhs);
         return;
     }
 
+    if (lhs->type() == MIRType_Int64) {
+        MOZ_ASSERT(rhs->type() == MIRType_Int64);
+        lowerForShiftInt64(new(alloc()) LShiftI64(op), ins, lhs, rhs);
+        return;
+    }
+
     MOZ_ASSERT(ins->specialization() == MIRType_None);
 
     if (op == JSOP_URSH) {
         // Result is either int32 or double so we have to use BinaryV.
         lowerBinaryV(JSOP_URSH, ins);
         return;
     }
 
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -2469,25 +2469,27 @@ MBinaryBitwiseInstruction::foldUnnecessa
 void
 MBinaryBitwiseInstruction::infer(BaselineInspector*, jsbytecode*)
 {
     if (getOperand(0)->mightBeType(MIRType_Object) || getOperand(0)->mightBeType(MIRType_Symbol) ||
         getOperand(1)->mightBeType(MIRType_Object) || getOperand(1)->mightBeType(MIRType_Symbol))
     {
         specialization_ = MIRType_None;
     } else {
-        specializeAsInt32();
+        specializeAs(MIRType_Int32);
     }
 }
 
 void
-MBinaryBitwiseInstruction::specializeAsInt32()
-{
-    specialization_ = MIRType_Int32;
-    MOZ_ASSERT(type() == MIRType_Int32);
+MBinaryBitwiseInstruction::specializeAs(MIRType type)
+{
+    MOZ_ASSERT(type == MIRType_Int32 || type == MIRType_Int64);
+    MOZ_ASSERT(this->type() == type);
+
+    specialization_ = type;
 
     if (isBitOr() || isBitAnd() || isBitXor())
         setCommutative();
 }
 
 void
 MShiftInstruction::infer(BaselineInspector*, jsbytecode*)
 {
@@ -3403,94 +3405,94 @@ MTypeOf::cacheInputMaybeCallableOrEmulat
 
     if (!input()->maybeEmulatesUndefined(constraints) && !MaybeCallable(constraints, input()))
         markInputNotCallableOrEmulatesUndefined();
 }
 
 MBitAnd*
 MBitAnd::New(TempAllocator& alloc, MDefinition* left, MDefinition* right)
 {
-    return new(alloc) MBitAnd(left, right);
+    return new(alloc) MBitAnd(left, right, MIRType_Int32);
 }
 
 MBitAnd*
-MBitAnd::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right)
-{
-    MBitAnd* ins = new(alloc) MBitAnd(left, right);
-    ins->specializeAsInt32();
+MBitAnd::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right, MIRType type)
+{
+    MBitAnd* ins = new(alloc) MBitAnd(left, right, type);
+    ins->specializeAs(type);
     return ins;
 }
 
 MBitOr*
 MBitOr::New(TempAllocator& alloc, MDefinition* left, MDefinition* right)
 {
-    return new(alloc) MBitOr(left, right);
+    return new(alloc) MBitOr(left, right, MIRType_Int32);
 }
 
 MBitOr*
-MBitOr::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right)
-{
-    MBitOr* ins = new(alloc) MBitOr(left, right);
-    ins->specializeAsInt32();
+MBitOr::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right, MIRType type)
+{
+    MBitOr* ins = new(alloc) MBitOr(left, right, type);
+    ins->specializeAs(type);
     return ins;
 }
 
 MBitXor*
 MBitXor::New(TempAllocator& alloc, MDefinition* left, MDefinition* right)
 {
-    return new(alloc) MBitXor(left, right);
+    return new(alloc) MBitXor(left, right, MIRType_Int32);
 }
 
 MBitXor*
-MBitXor::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right)
-{
-    MBitXor* ins = new(alloc) MBitXor(left, right);
-    ins->specializeAsInt32();
+MBitXor::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right, MIRType type)
+{
+    MBitXor* ins = new(alloc) MBitXor(left, right, type);
+    ins->specializeAs(type);
     return ins;
 }
 
 MLsh*
 MLsh::New(TempAllocator& alloc, MDefinition* left, MDefinition* right)
 {
-    return new(alloc) MLsh(left, right);
+    return new(alloc) MLsh(left, right, MIRType_Int32);
 }
 
 MLsh*
-MLsh::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right)
-{
-    MLsh* ins = new(alloc) MLsh(left, right);
-    ins->specializeAsInt32();
+MLsh::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right, MIRType type)
+{
+    MLsh* ins = new(alloc) MLsh(left, right, type);
+    ins->specializeAs(type);
     return ins;
 }
 
 MRsh*
 MRsh::New(TempAllocator& alloc, MDefinition* left, MDefinition* right)
 {
-    return new(alloc) MRsh(left, right);
+    return new(alloc) MRsh(left, right, MIRType_Int32);
 }
 
 MRsh*
-MRsh::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right)
-{
-    MRsh* ins = new(alloc) MRsh(left, right);
-    ins->specializeAsInt32();
+MRsh::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right, MIRType type)
+{
+    MRsh* ins = new(alloc) MRsh(left, right, type);
+    ins->specializeAs(type);
     return ins;
 }
 
 MUrsh*
 MUrsh::New(TempAllocator& alloc, MDefinition* left, MDefinition* right)
 {
-    return new(alloc) MUrsh(left, right);
+    return new(alloc) MUrsh(left, right, MIRType_Int32);
 }
 
 MUrsh*
-MUrsh::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right)
-{
-    MUrsh* ins = new(alloc) MUrsh(left, right);
-    ins->specializeAsInt32();
+MUrsh::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right, MIRType type)
+{
+    MUrsh* ins = new(alloc) MUrsh(left, right, type);
+    ins->specializeAs(type);
 
     // Since Ion has no UInt32 type, we use Int32 and we have a special
     // exception to the type rules: we can return values in
     // (INT32_MIN,UINT32_MAX] and still claim that we have an Int32 type
     // without bailing out. This is necessary because Ion has no UInt32
     // type and we can't have bailouts in asm.js code.
     ins->bailoutsDisabled_ = true;
 
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -5570,25 +5570,26 @@ class MToId
     }
 };
 
 class MBinaryBitwiseInstruction
   : public MBinaryInstruction,
     public BitwisePolicy::Data
 {
   protected:
-    MBinaryBitwiseInstruction(MDefinition* left, MDefinition* right)
+    MBinaryBitwiseInstruction(MDefinition* left, MDefinition* right, MIRType type)
       : MBinaryInstruction(left, right), maskMatchesLeftRange(false),
         maskMatchesRightRange(false)
     {
-        setResultType(MIRType_Int32);
-        setMovable();
-    }
-
-    void specializeAsInt32();
+        MOZ_ASSERT(type == MIRType_Int32 || type == MIRType_Int64);
+        setResultType(type);
+        setMovable();
+    }
+
+    void specializeAs(MIRType type);
     bool maskMatchesLeftRange;
     bool maskMatchesRightRange;
 
   public:
     MDefinition* foldsTo(TempAllocator& alloc) override;
     MDefinition* foldUnnecessaryBitop();
     virtual MDefinition* foldIfZero(size_t operand) = 0;
     virtual MDefinition* foldIfNegOne(size_t operand) = 0;
@@ -5611,24 +5612,25 @@ class MBinaryBitwiseInstruction
         return AliasSet::None();
     }
 
     TruncateKind operandTruncateKind(size_t index) const override;
 };
 
 class MBitAnd : public MBinaryBitwiseInstruction
 {
-    MBitAnd(MDefinition* left, MDefinition* right)
-      : MBinaryBitwiseInstruction(left, right)
+    MBitAnd(MDefinition* left, MDefinition* right, MIRType type)
+      : MBinaryBitwiseInstruction(left, right, type)
     { }
 
   public:
     INSTRUCTION_HEADER(BitAnd)
     static MBitAnd* New(TempAllocator& alloc, MDefinition* left, MDefinition* right);
-    static MBitAnd* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right);
+    static MBitAnd* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right,
+                             MIRType type);
 
     MDefinition* foldIfZero(size_t operand) override {
         return getOperand(operand); // 0 & x => 0;
     }
     MDefinition* foldIfNegOne(size_t operand) override {
         return getOperand(1 - operand); // x & -1 => x
     }
     MDefinition* foldIfEqual() override {
@@ -5645,24 +5647,25 @@ class MBitAnd : public MBinaryBitwiseIns
         return specialization_ != MIRType_None;
     }
 
     ALLOW_CLONE(MBitAnd)
 };
 
 class MBitOr : public MBinaryBitwiseInstruction
 {
-    MBitOr(MDefinition* left, MDefinition* right)
-      : MBinaryBitwiseInstruction(left, right)
+    MBitOr(MDefinition* left, MDefinition* right, MIRType type)
+      : MBinaryBitwiseInstruction(left, right, type)
     { }
 
   public:
     INSTRUCTION_HEADER(BitOr)
     static MBitOr* New(TempAllocator& alloc, MDefinition* left, MDefinition* right);
-    static MBitOr* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right);
+    static MBitOr* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right,
+                            MIRType type);
 
     MDefinition* foldIfZero(size_t operand) override {
         return getOperand(1 - operand); // 0 | x => x, so if ith is 0, return (1-i)th
     }
     MDefinition* foldIfNegOne(size_t operand) override {
         return getOperand(operand); // x | -1 => -1
     }
     MDefinition* foldIfEqual() override {
@@ -5677,24 +5680,25 @@ class MBitOr : public MBinaryBitwiseInst
         return specialization_ != MIRType_None;
     }
 
     ALLOW_CLONE(MBitOr)
 };
 
 class MBitXor : public MBinaryBitwiseInstruction
 {
-    MBitXor(MDefinition* left, MDefinition* right)
-      : MBinaryBitwiseInstruction(left, right)
+    MBitXor(MDefinition* left, MDefinition* right, MIRType type)
+      : MBinaryBitwiseInstruction(left, right, type)
     { }
 
   public:
     INSTRUCTION_HEADER(BitXor)
     static MBitXor* New(TempAllocator& alloc, MDefinition* left, MDefinition* right);
-    static MBitXor* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right);
+    static MBitXor* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right,
+                             MIRType type);
 
     MDefinition* foldIfZero(size_t operand) override {
         return getOperand(1 - operand); // 0 ^ x => x
     }
     MDefinition* foldIfNegOne(size_t operand) override {
         return this;
     }
     MDefinition* foldIfEqual() override {
@@ -5712,18 +5716,18 @@ class MBitXor : public MBinaryBitwiseIns
 
     ALLOW_CLONE(MBitXor)
 };
 
 class MShiftInstruction
   : public MBinaryBitwiseInstruction
 {
   protected:
-    MShiftInstruction(MDefinition* left, MDefinition* right)
-      : MBinaryBitwiseInstruction(left, right)
+    MShiftInstruction(MDefinition* left, MDefinition* right, MIRType type)
+      : MBinaryBitwiseInstruction(left, right, type)
     { }
 
   public:
     MDefinition* foldIfNegOne(size_t operand) override {
         return this;
     }
     MDefinition* foldIfEqual() override {
         return this;
@@ -5731,24 +5735,25 @@ class MShiftInstruction
     MDefinition* foldIfAllBitsSet(size_t operand) override {
         return this;
     }
     virtual void infer(BaselineInspector* inspector, jsbytecode* pc) override;
 };
 
 class MLsh : public MShiftInstruction
 {
-    MLsh(MDefinition* left, MDefinition* right)
-      : MShiftInstruction(left, right)
+    MLsh(MDefinition* left, MDefinition* right, MIRType type)
+      : MShiftInstruction(left, right, type)
     { }
 
   public:
     INSTRUCTION_HEADER(Lsh)
     static MLsh* New(TempAllocator& alloc, MDefinition* left, MDefinition* right);
-    static MLsh* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right);
+    static MLsh* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right,
+                          MIRType type);
 
     MDefinition* foldIfZero(size_t operand) override {
         // 0 << x => 0
         // x << 0 => x
         return getOperand(0);
     }
 
     void computeRange(TempAllocator& alloc) override;
@@ -5757,24 +5762,25 @@ class MLsh : public MShiftInstruction
         return specialization_ != MIRType_None;
     }
 
     ALLOW_CLONE(MLsh)
 };
 
 class MRsh : public MShiftInstruction
 {
-    MRsh(MDefinition* left, MDefinition* right)
-      : MShiftInstruction(left, right)
+    MRsh(MDefinition* left, MDefinition* right, MIRType type)
+      : MShiftInstruction(left, right, type)
     { }
 
   public:
     INSTRUCTION_HEADER(Rsh)
     static MRsh* New(TempAllocator& alloc, MDefinition* left, MDefinition* right);
-    static MRsh* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right);
+    static MRsh* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right,
+                          MIRType type);
 
     MDefinition* foldIfZero(size_t operand) override {
         // 0 >> x => 0
         // x >> 0 => x
         return getOperand(0);
     }
     void computeRange(TempAllocator& alloc) override;
 
@@ -5785,25 +5791,26 @@ class MRsh : public MShiftInstruction
 
     ALLOW_CLONE(MRsh)
 };
 
 class MUrsh : public MShiftInstruction
 {
     bool bailoutsDisabled_;
 
-    MUrsh(MDefinition* left, MDefinition* right)
-      : MShiftInstruction(left, right),
+    MUrsh(MDefinition* left, MDefinition* right, MIRType type)
+      : MShiftInstruction(left, right, type),
         bailoutsDisabled_(false)
     { }
 
   public:
     INSTRUCTION_HEADER(Ursh)
     static MUrsh* New(TempAllocator& alloc, MDefinition* left, MDefinition* right);
-    static MUrsh* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right);
+    static MUrsh* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right,
+                           MIRType type);
 
     MDefinition* foldIfZero(size_t operand) override {
         // 0 >>> x => 0
         if (operand == 0)
             return getOperand(0);
 
         return this;
     }
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -688,16 +688,18 @@ class MacroAssembler : public MacroAssem
     inline void and32(Imm32 imm, Register src, Register dest) DEFINED_ON(arm64);
     inline void and32(Imm32 imm, const Address& dest) PER_SHARED_ARCH;
     inline void and32(const Address& src, Register dest) PER_SHARED_ARCH;
 
     inline void andPtr(Register src, Register dest) PER_ARCH;
     inline void andPtr(Imm32 imm, Register dest) PER_ARCH;
 
     inline void and64(Imm64 imm, Register64 dest) PER_ARCH;
+    inline void or64(Imm64 imm, Register64 dest) PER_ARCH;
+    inline void xor64(Imm64 imm, Register64 dest) PER_ARCH;
 
     inline void or32(Register src, Register dest) PER_SHARED_ARCH;
     inline void or32(Imm32 imm, Register dest) PER_SHARED_ARCH;
     inline void or32(Imm32 imm, const Address& dest) PER_SHARED_ARCH;
 
     inline void orPtr(Register src, Register dest) PER_ARCH;
     inline void orPtr(Imm32 imm, Register dest) PER_ARCH;
 
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -628,18 +628,23 @@ Range::Range(const MDefinition* def)
         }
     }
 
     // As a special case, MUrsh is permitted to claim a result type of
     // MIRType_Int32 while actually returning values in [0,UINT32_MAX] without
     // bailouts. If range analysis hasn't ruled out values in
     // (INT32_MAX,UINT32_MAX], set the range to be conservatively correct for
     // use as either a uint32 or an int32.
-    if (!hasInt32UpperBound() && def->isUrsh() && def->toUrsh()->bailoutsDisabled())
+    if (!hasInt32UpperBound() &&
+        def->isUrsh() &&
+        def->toUrsh()->bailoutsDisabled() &&
+        def->type() != MIRType_Int64)
+    {
         lower_ = INT32_MIN;
+    }
 
     assertInvariants();
 }
 
 static uint16_t
 ExponentImpliedByDouble(double d)
 {
     // Handle the special values.
@@ -1311,38 +1316,47 @@ void
 MClampToUint8::computeRange(TempAllocator& alloc)
 {
     setRange(Range::NewUInt32Range(alloc, 0, 255));
 }
 
 void
 MBitAnd::computeRange(TempAllocator& alloc)
 {
+    if (specialization_ == MIRType_Int64)
+        return;
+
     Range left(getOperand(0));
     Range right(getOperand(1));
     left.wrapAroundToInt32();
     right.wrapAroundToInt32();
 
     setRange(Range::and_(alloc, &left, &right));
 }
 
 void
 MBitOr::computeRange(TempAllocator& alloc)
 {
+    if (specialization_ == MIRType_Int64)
+        return;
+
     Range left(getOperand(0));
     Range right(getOperand(1));
     left.wrapAroundToInt32();
     right.wrapAroundToInt32();
 
     setRange(Range::or_(alloc, &left, &right));
 }
 
 void
 MBitXor::computeRange(TempAllocator& alloc)
 {
+    if (specialization_ == MIRType_Int64)
+        return;
+
     Range left(getOperand(0));
     Range right(getOperand(1));
     left.wrapAroundToInt32();
     right.wrapAroundToInt32();
 
     setRange(Range::xor_(alloc, &left, &right));
 }
 
@@ -1353,16 +1367,19 @@ MBitNot::computeRange(TempAllocator& all
     op.wrapAroundToInt32();
 
     setRange(Range::not_(alloc, &op));
 }
 
 void
 MLsh::computeRange(TempAllocator& alloc)
 {
+    if (specialization_ == MIRType_Int64)
+        return;
+
     Range left(getOperand(0));
     Range right(getOperand(1));
     left.wrapAroundToInt32();
 
     MConstant* rhsConst = getOperand(1)->maybeConstantValue();
     if (rhsConst && rhsConst->type() == MIRType_Int32) {
         int32_t c = rhsConst->toInt32();
         setRange(Range::lsh(alloc, &left, c));
@@ -1371,16 +1388,19 @@ MLsh::computeRange(TempAllocator& alloc)
 
     right.wrapAroundToShiftCount();
     setRange(Range::lsh(alloc, &left, &right));
 }
 
 void
 MRsh::computeRange(TempAllocator& alloc)
 {
+    if (specialization_ == MIRType_Int64)
+        return;
+
     Range left(getOperand(0));
     Range right(getOperand(1));
     left.wrapAroundToInt32();
 
     MConstant* rhsConst = getOperand(1)->maybeConstantValue();
     if (rhsConst && rhsConst->type() == MIRType_Int32) {
         int32_t c = rhsConst->toInt32();
         setRange(Range::rsh(alloc, &left, c));
@@ -1389,16 +1409,19 @@ MRsh::computeRange(TempAllocator& alloc)
 
     right.wrapAroundToShiftCount();
     setRange(Range::rsh(alloc, &left, &right));
 }
 
 void
 MUrsh::computeRange(TempAllocator& alloc)
 {
+    if (specialization_ == MIRType_Int64)
+        return;
+
     Range left(getOperand(0));
     Range right(getOperand(1));
 
     // ursh can be thought of as converting its left operand to uint32, or it
     // can be thought of as converting its left operand to int32, and then
     // reinterpreting the int32 bits as a uint32 value. Both approaches yield
     // the same result. Since we lack support for full uint32 ranges, we use
     // the second interpretation, though it does cause us to be conservative.
@@ -3318,16 +3341,19 @@ MPowHalf::collectRangeInfoPreTrunc()
         operandIsNeverNegativeZero_ = true;
     if (!inputRange.canBeNaN())
         operandIsNeverNaN_ = true;
 }
 
 void
 MUrsh::collectRangeInfoPreTrunc()
 {
+    if (specialization_ == MIRType_Int64)
+        return;
+
     Range lhsRange(lhs()), rhsRange(rhs());
 
     // As in MUrsh::computeRange(), convert the inputs.
     lhsRange.wrapAroundToInt32();
     rhsRange.wrapAroundToShiftCount();
 
     // If the most significant bit of our result is always going to be zero,
     // we can optimize by disabling bailout checks for enforcing an int32 range.
--- a/js/src/jit/arm/Lowering-arm.cpp
+++ b/js/src/jit/arm/Lowering-arm.cpp
@@ -159,16 +159,23 @@ LIRGeneratorARM::lowerForALU(LInstructio
     // MulI, but only for bail out paths so useAtStart when no bailouts.
     ins->setOperand(0, ins->snapshot() ? useRegister(lhs) : useRegisterAtStart(lhs));
     ins->setOperand(1, ins->snapshot() ? useRegisterOrConstant(rhs) :
                                          useRegisterOrConstantAtStart(rhs));
     define(ins, mir, LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER));
 }
 
 void
+LIRGeneratorARM::lowerForALUInt64(LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>* ins,
+                                  MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
+{
+    MOZ_CRASH("NYI");
+}
+
+void
 LIRGeneratorARM::lowerForFPU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir, MDefinition* input)
 {
     ins->setOperand(0, useRegisterAtStart(input));
     define(ins, mir, LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER));
 }
 
 template<size_t Temps>
 void
@@ -226,16 +233,23 @@ void
 LIRGeneratorARM::lowerForShift(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
 {
     ins->setOperand(0, useRegister(lhs));
     ins->setOperand(1, useRegisterOrConstant(rhs));
     define(ins, mir);
 }
 
 void
+LIRGeneratorARM::lowerForShiftInt64(LInstructionHelper<INT64_PIECES, INT64_PIECES + 1, 0>* ins,
+                                    MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
+{
+    MOZ_CRASH("NYI");
+}
+
+void
 LIRGeneratorARM::lowerDivI(MDiv* div)
 {
     if (div->isUnsigned()) {
         lowerUDiv(div);
         return;
     }
 
     // Division instructions are slow. Division by constant denominators can be
--- a/js/src/jit/arm/Lowering-arm.h
+++ b/js/src/jit/arm/Lowering-arm.h
@@ -42,16 +42,21 @@ class LIRGeneratorARM : public LIRGenera
                        MDefinition* rhs);
     void lowerUrshD(MUrsh* mir);
 
     void lowerForALU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir,
                      MDefinition* input);
     void lowerForALU(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir,
                      MDefinition* lhs, MDefinition* rhs);
 
+    void lowerForALUInt64(LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>* ins, MDefinition* mir,
+                          MDefinition* lhs, MDefinition* rhs);
+    void lowerForShiftInt64(LInstructionHelper<INT64_PIECES, INT64_PIECES + 1, 0>* ins,
+                            MDefinition* mir, MDefinition* lhs, MDefinition* rhs);
+
     void lowerForFPU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir,
                      MDefinition* src);
     template<size_t Temps>
     void lowerForFPU(LInstructionHelper<1, 2, Temps>* ins, MDefinition* mir,
                      MDefinition* lhs, MDefinition* rhs);
 
     void lowerForCompIx4(LSimdBinaryCompIx4* ins, MSimdBinaryComp* mir,
                          MDefinition* lhs, MDefinition* rhs)
--- a/js/src/jit/arm/MacroAssembler-arm-inl.h
+++ b/js/src/jit/arm/MacroAssembler-arm-inl.h
@@ -81,16 +81,30 @@ MacroAssembler::andPtr(Imm32 imm, Regist
 void
 MacroAssembler::and64(Imm64 imm, Register64 dest)
 {
     and32(Imm32(imm.value & 0xFFFFFFFFL), dest.low);
     and32(Imm32((imm.value >> 32) & 0xFFFFFFFFL), dest.high);
 }
 
 void
+MacroAssembler::or64(Imm64 imm, Register64 dest)
+{
+    or32(Imm32(imm.value & 0xFFFFFFFFL), dest.low);
+    or32(Imm32((imm.value >> 32) & 0xFFFFFFFFL), dest.high);
+}
+
+void
+MacroAssembler::xor64(Imm64 imm, Register64 dest)
+{
+    xor32(Imm32(imm.value & 0xFFFFFFFFL), dest.low);
+    xor32(Imm32((imm.value >> 32) & 0xFFFFFFFFL), dest.high);
+}
+
+void
 MacroAssembler::or32(Register src, Register dest)
 {
     ma_orr(src, dest);
 }
 
 void
 MacroAssembler::or32(Imm32 imm, Register dest)
 {
--- a/js/src/jit/arm64/Lowering-arm64.cpp
+++ b/js/src/jit/arm64/Lowering-arm64.cpp
@@ -83,16 +83,30 @@ LIRGeneratorARM64::lowerForFPU(LInstruct
 }
 
 template void LIRGeneratorARM64::lowerForFPU(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir,
                                              MDefinition* lhs, MDefinition* rhs);
 template void LIRGeneratorARM64::lowerForFPU(LInstructionHelper<1, 2, 1>* ins, MDefinition* mir,
                                              MDefinition* lhs, MDefinition* rhs);
 
 void
+LIRGeneratorARM64::lowerForALUInt64(LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>* ins,
+                                    MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
+{
+    MOZ_CRASH("NYI");
+}
+
+void
+LIRGeneratorARM64::lowerForShiftInt64(LInstructionHelper<INT64_PIECES, INT64_PIECES + 1, 0>* ins,
+                                      MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
+{
+    MOZ_CRASH("NYI");
+}
+
+void
 LIRGeneratorARM64::lowerForBitAndAndBranch(LBitAndAndBranch* baab, MInstruction* mir,
                                          MDefinition* lhs, MDefinition* rhs)
 {
     MOZ_CRASH("lowerForBitAndAndBranch");
 }
 
 void
 LIRGeneratorARM64::defineUntypedPhi(MPhi* phi, size_t lirIndex)
--- a/js/src/jit/arm64/Lowering-arm64.h
+++ b/js/src/jit/arm64/Lowering-arm64.h
@@ -43,16 +43,21 @@ class LIRGeneratorARM64 : public LIRGene
     void lowerForShift(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir, MDefinition* lhs,
                        MDefinition* rhs);
     void lowerUrshD(MUrsh* mir);
 
     void lowerForALU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir, MDefinition* input);
     void lowerForALU(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir,
                      MDefinition* lhs, MDefinition* rhs);
 
+    void lowerForALUInt64(LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>* ins, MDefinition* mir,
+                          MDefinition* lhs, MDefinition* rhs);
+    void lowerForShiftInt64(LInstructionHelper<INT64_PIECES, INT64_PIECES + 1, 0>* ins,
+                            MDefinition* mir, MDefinition* lhs, MDefinition* rhs);
+
     void lowerForFPU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir, MDefinition* input);
 
     template <size_t Temps>
     void lowerForFPU(LInstructionHelper<1, 2, Temps>* ins, MDefinition* mir,
                      MDefinition* lhs, MDefinition* rhs);
 
     void lowerForCompIx4(LSimdBinaryCompIx4* ins, MSimdBinaryComp* mir,
                          MDefinition* lhs, MDefinition* rhs)
--- a/js/src/jit/arm64/MacroAssembler-arm64-inl.h
+++ b/js/src/jit/arm64/MacroAssembler-arm64-inl.h
@@ -91,16 +91,34 @@ MacroAssembler::and64(Imm64 imm, Registe
 {
     vixl::UseScratchRegisterScope temps(this);
     const Register scratch = temps.AcquireX().asUnsized();
     mov(ImmWord(imm.value), scratch);
     andPtr(scratch, dest.reg);
 }
 
 void
+MacroAssembler::or64(Imm64 imm, Register64 dest)
+{
+    vixl::UseScratchRegisterScope temps(this);
+    const Register scratch = temps.AcquireX().asUnsized();
+    mov(ImmWord(imm.value), scratch);
+    orPtr(scratch, dest.reg);
+}
+
+void
+MacroAssembler::xor64(Imm64 imm, Register64 dest)
+{
+    vixl::UseScratchRegisterScope temps(this);
+    const Register scratch = temps.AcquireX().asUnsized();
+    mov(ImmWord(imm.value), scratch);
+    xorPtr(scratch, dest.reg);
+}
+
+void
 MacroAssembler::or32(Imm32 imm, Register dest)
 {
     Orr(ARMRegister(dest, 32), ARMRegister(dest, 32), Operand(imm.value));
 }
 
 void
 MacroAssembler::or32(Register src, Register dest)
 {
--- a/js/src/jit/mips-shared/Lowering-mips-shared.cpp
+++ b/js/src/jit/mips-shared/Lowering-mips-shared.cpp
@@ -50,16 +50,30 @@ LIRGeneratorMIPSShared::lowerForALU(LIns
                                     MDefinition* lhs, MDefinition* rhs)
 {
     ins->setOperand(0, useRegister(lhs));
     ins->setOperand(1, useRegisterOrConstant(rhs));
     define(ins, mir, LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER));
 }
 
 void
+LIRGeneratorMIPSShared::lowerForALUInt64(LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>* ins,
+                                         MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
+{
+    MOZ_CRASH("NYI");
+}
+
+void
+LIRGeneratorMIPSShared::lowerForShiftInt64(LInstructionHelper<INT64_PIECES, INT64_PIECES + 1, 0>* ins,
+                                           MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
+{
+    MOZ_CRASH("NYI");
+}
+
+void
 LIRGeneratorMIPSShared::lowerForFPU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir,
                                     MDefinition* input)
 {
     ins->setOperand(0, useRegister(input));
     define(ins, mir, LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER));
 }
 
 template<size_t Temps>
--- a/js/src/jit/mips-shared/Lowering-mips-shared.h
+++ b/js/src/jit/mips-shared/Lowering-mips-shared.h
@@ -32,16 +32,21 @@ class LIRGeneratorMIPSShared : public LI
                        MDefinition* rhs);
     void lowerUrshD(MUrsh* mir);
 
     void lowerForALU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir,
                      MDefinition* input);
     void lowerForALU(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir,
                      MDefinition* lhs, MDefinition* rhs);
 
+    void lowerForALUInt64(LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>* ins, MDefinition* mir,
+                          MDefinition* lhs, MDefinition* rhs);
+    void lowerForShiftInt64(LInstructionHelper<INT64_PIECES, INT64_PIECES + 1, 0>* ins,
+                            MDefinition* mir, MDefinition* lhs, MDefinition* rhs);
+
     void lowerForFPU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir,
                      MDefinition* src);
     template<size_t Temps>
     void lowerForFPU(LInstructionHelper<1, 2, Temps>* ins, MDefinition* mir,
                      MDefinition* lhs, MDefinition* rhs);
 
     void lowerForCompIx4(LSimdBinaryCompIx4* ins, MSimdBinaryComp* mir,
                          MDefinition* lhs, MDefinition* rhs)
--- a/js/src/jit/mips32/MacroAssembler-mips32-inl.h
+++ b/js/src/jit/mips32/MacroAssembler-mips32-inl.h
@@ -48,16 +48,30 @@ MacroAssembler::andPtr(Imm32 imm, Regist
 void
 MacroAssembler::and64(Imm64 imm, Register64 dest)
 {
     and32(Imm32(imm.value & LOW_32_MASK), dest.low);
     and32(Imm32((imm.value >> 32) & LOW_32_MASK), dest.high);
 }
 
 void
+MacroAssembler::or64(Imm64 imm, Register64 dest)
+{
+    or32(Imm32(imm.value & LOW_32_MASK), dest.low);
+    or32(Imm32((imm.value >> 32) & LOW_32_MASK), dest.high);
+}
+
+void
+MacroAssembler::xor64(Imm64 imm, Register64 dest)
+{
+    xor32(Imm32(imm.value & LOW_32_MASK), dest.low);
+    xor32(Imm32((imm.value >> 32) & LOW_32_MASK), dest.high);
+}
+
+void
 MacroAssembler::orPtr(Register src, Register dest)
 {
     ma_or(dest, src);
 }
 
 void
 MacroAssembler::orPtr(Imm32 imm, Register dest)
 {
--- a/js/src/jit/mips64/MacroAssembler-mips64-inl.h
+++ b/js/src/jit/mips64/MacroAssembler-mips64-inl.h
@@ -46,16 +46,30 @@ MacroAssembler::andPtr(Imm32 imm, Regist
 void
 MacroAssembler::and64(Imm64 imm, Register64 dest)
 {
     ma_li(ScratchRegister, ImmWord(imm.value));
     ma_and(dest.reg, ScratchRegister);
 }
 
 void
+MacroAssembler::or64(Imm64 imm, Register64 dest)
+{
+    ma_li(ScratchRegister, ImmWord(imm.value));
+    ma_or(dest.reg, ScratchRegister);
+}
+
+void
+MacroAssembler::xor64(Imm64 imm, Register64 dest)
+{
+    ma_li(ScratchRegister, ImmWord(imm.value));
+    ma_xor(dest.reg, ScratchRegister);
+}
+
+void
 MacroAssembler::orPtr(Register src, Register dest)
 {
     ma_or(dest, src);
 }
 
 void
 MacroAssembler::orPtr(Imm32 imm, Register dest)
 {
--- a/js/src/jit/none/Lowering-none.h
+++ b/js/src/jit/none/Lowering-none.h
@@ -33,16 +33,20 @@ class LIRGeneratorNone : public LIRGener
     void lowerForShift(LInstructionHelper<1, 2, 0>*, MDefinition*, MDefinition*, MDefinition*) {
         MOZ_CRASH();
     }
     void lowerUrshD(MUrsh*) { MOZ_CRASH(); }
     template <typename T>
     void lowerForALU(T, MDefinition*, MDefinition*, MDefinition* v = nullptr) { MOZ_CRASH(); }
     template <typename T>
     void lowerForFPU(T, MDefinition*, MDefinition*, MDefinition* v = nullptr) { MOZ_CRASH(); }
+    template <typename T>
+    void lowerForALUInt64(T, MDefinition*, MDefinition*, MDefinition* v = nullptr) { MOZ_CRASH(); }
+    template <typename T>
+    void lowerForShiftInt64(T, MDefinition*, MDefinition*, MDefinition* v = nullptr) { MOZ_CRASH(); }
     void lowerForCompIx4(LSimdBinaryCompIx4* ins, MSimdBinaryComp* mir,
                          MDefinition* lhs, MDefinition* rhs) {
         MOZ_CRASH();
     }
     void lowerForCompFx4(LSimdBinaryCompFx4* ins, MSimdBinaryComp* mir,
                          MDefinition* lhs, MDefinition* rhs) {
         MOZ_CRASH();
     }
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -2943,16 +2943,39 @@ class LBitOpI : public LInstructionHelpe
         return CodeName[op_];
     }
 
     JSOp bitop() const {
         return op_;
     }
 };
 
+class LBitOpI64 : public LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>
+{
+    JSOp op_;
+
+  public:
+    LIR_HEADER(BitOpI64)
+
+    static const size_t Lhs = 0;
+    static const size_t Rhs = INT64_PIECES;
+
+    explicit LBitOpI64(JSOp op)
+      : op_(op)
+    { }
+
+    const char* extraName() const {
+        return CodeName[op_];
+    }
+
+    JSOp bitop() const {
+        return op_;
+    }
+};
+
 // Call a VM function to perform a bitwise operation.
 class LBitOpV : public LCallInstructionHelper<1, 2 * BOX_PIECES, 0>
 {
     JSOp jsop_;
 
   public:
     LIR_HEADER(BitOpV)
 
@@ -2996,16 +3019,40 @@ class LShiftI : public LBinaryMath<0>
         return mir_->toInstruction();
     }
 
     const char* extraName() const {
         return CodeName[op_];
     }
 };
 
+class LShiftI64 : public LInstructionHelper<INT64_PIECES, INT64_PIECES + 1, 0>
+{
+    JSOp op_;
+
+  public:
+    LIR_HEADER(ShiftI64)
+
+    explicit LShiftI64(JSOp op)
+      : op_(op)
+    { }
+
+    JSOp bitop() {
+        return op_;
+    }
+
+    MInstruction* mir() {
+        return mir_->toInstruction();
+    }
+
+    const char* extraName() const {
+        return CodeName[op_];
+    }
+};
+
 class LUrshD : public LBinaryMath<1>
 {
   public:
     LIR_HEADER(UrshD)
 
     LUrshD(const LAllocation& lhs, const LAllocation& rhs, const LDefinition& temp) {
         setOperand(0, lhs);
         setOperand(1, rhs);
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -92,18 +92,20 @@
     _(CreateArgumentsObject)        \
     _(GetArgumentsObjectArg)        \
     _(SetArgumentsObjectArg)        \
     _(ReturnFromCtor)               \
     _(ComputeThis)                  \
     _(BitNotI)                      \
     _(BitNotV)                      \
     _(BitOpI)                       \
+    _(BitOpI64)                     \
     _(BitOpV)                       \
     _(ShiftI)                       \
+    _(ShiftI64)                     \
     _(UrshD)                        \
     _(Return)                       \
     _(Throw)                        \
     _(Phi)                          \
     _(TestIAndBranch)               \
     _(TestDAndBranch)               \
     _(TestFAndBranch)               \
     _(TestVAndBranch)               \
--- a/js/src/jit/shared/Lowering-shared-inl.h
+++ b/js/src/jit/shared/Lowering-shared-inl.h
@@ -24,16 +24,17 @@ LIRGeneratorShared::emitAtUses(MInstruct
 }
 
 LUse
 LIRGeneratorShared::use(MDefinition* mir, LUse policy)
 {
     // It is illegal to call use() on an instruction with two defs.
 #if BOX_PIECES > 1
     MOZ_ASSERT(mir->type() != MIRType_Value);
+    MOZ_ASSERT(mir->type() != MIRType_Int64);
 #endif
     ensureDefined(mir);
     policy.setVirtualRegister(mir->virtualRegister());
     return policy;
 }
 
 template <size_t X> void
 LIRGeneratorShared::define(details::LInstructionFixedDefsTempsHelper<1, X>* lir, MDefinition* mir,
@@ -82,21 +83,51 @@ LIRGeneratorShared::defineReuseInput(LIn
 
     LDefinition def(type, LDefinition::MUST_REUSE_INPUT);
     def.setReusedInput(operand);
 
     define(lir, mir, def);
 }
 
 template <size_t Ops, size_t Temps> void
+LIRGeneratorShared::defineInt64ReuseInput(LInstructionHelper<INT64_PIECES, 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());
+    MOZ_ASSERT(!lir->isCall());
+
+    uint32_t vreg = getVirtualRegister();
+
+    LDefinition def1(LDefinition::GENERAL, LDefinition::MUST_REUSE_INPUT);
+    def1.setReusedInput(operand);
+    lir->setDef(0, def1);
+    lir->getDef(0)->setVirtualRegister(vreg);
+
+#if JS_BITS_PER_WORD == 32
+    getVirtualRegister();
+    LDefinition def2(LDefinition::GENERAL, LDefinition::MUST_REUSE_INPUT);
+    def2.setReusedInput(operand + 1);
+    lir->setDef(1, def2);
+    lir->getDef(1)->setVirtualRegister(vreg + 1);
+#endif
+
+    lir->setMir(mir);
+    mir->setVirtualRegister(vreg);
+    add(lir);
+
+}
+
+template <size_t Ops, size_t Temps> void
 LIRGeneratorShared::defineBox(LInstructionHelper<BOX_PIECES, Ops, Temps>* lir, MDefinition* mir,
                               LDefinition::Policy policy)
 {
     // Call instructions should use defineReturn.
     MOZ_ASSERT(!lir->isCall());
+    MOZ_ASSERT(mir->type() == MIRType_Value);
 
     uint32_t vreg = getVirtualRegister();
 
 #if defined(JS_NUNBOX32)
     lir->setDef(0, LDefinition(vreg + VREG_TYPE_OFFSET, LDefinition::TYPE, policy));
     lir->setDef(1, LDefinition(vreg + VREG_DATA_OFFSET, LDefinition::PAYLOAD, policy));
     getVirtualRegister();
 #elif defined(JS_PUNBOX64)
@@ -109,16 +140,17 @@ LIRGeneratorShared::defineBox(LInstructi
 }
 
 template <size_t Ops, size_t Temps> void
 LIRGeneratorShared::defineInt64(LInstructionHelper<INT64_PIECES, Ops, Temps>* lir, MDefinition* mir,
                                 LDefinition::Policy policy)
 {
     // Call instructions should use defineReturn.
     MOZ_ASSERT(!lir->isCall());
+    MOZ_ASSERT(mir->type() == MIRType_Int64);
 
     uint32_t vreg = getVirtualRegister();
 
 #if JS_BITS_PER_WORD == 32
     lir->setDef(0, LDefinition(vreg, LDefinition::GENERAL, policy));
     lir->setDef(1, LDefinition(vreg + 1, LDefinition::GENERAL, policy));
     getVirtualRegister();
 #else
@@ -676,41 +708,41 @@ LIRGeneratorShared::useInt64(MDefinition
     return LInt64Allocation(LUse(vreg + 1, policy, useAtStart),
                             LUse(vreg, policy, useAtStart));
 #else
     return LInt64Allocation(LUse(vreg, policy, useAtStart));
 #endif
 }
 
 LInt64Allocation
-LIRGeneratorShared::useInt64(MDefinition* mir)
+LIRGeneratorShared::useInt64(MDefinition* mir, bool useAtStart)
 {
     // On 32-bit platforms, always load the value in registers.
 #if JS_BITS_PER_WORD == 32
-    return useInt64(mir, LUse::REGISTER, /* useAtStart = */ false);
+    return useInt64(mir, LUse::REGISTER, useAtStart);
 #else
-    return useInt64(mir, LUse::ANY, /* useAtStart = */ false);
+    return useInt64(mir, LUse::ANY, useAtStart);
 #endif
 }
 
 LInt64Allocation
 LIRGeneratorShared::useInt64Register(MDefinition* mir, bool useAtStart)
 {
     return useInt64(mir, LUse::REGISTER, useAtStart);
 }
 
 LInt64Allocation
-LIRGeneratorShared::useInt64OrConstant(MDefinition* mir)
+LIRGeneratorShared::useInt64OrConstant(MDefinition* mir, bool useAtStart)
 {
     if (mir->isConstant()) {
 #if defined(JS_NUNBOX32)
         return LInt64Allocation(LAllocation(mir->toConstant()), LAllocation());
 #else
         return LInt64Allocation(LAllocation(mir->toConstant()));
 #endif
     }
-    return useInt64(mir);
+    return useInt64(mir, useAtStart);
 }
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_shared_Lowering_shared_inl_h */
--- a/js/src/jit/shared/Lowering-shared.h
+++ b/js/src/jit/shared/Lowering-shared.h
@@ -158,30 +158,41 @@ class LIRGeneratorShared : public MDefin
                        LDefinition::Policy policy = LDefinition::REGISTER);
     template <size_t X>
     inline void define(details::LInstructionFixedDefsTempsHelper<1, X>* lir, MDefinition* mir,
                        const LDefinition& def);
 
     template <size_t Ops, size_t Temps>
     inline void defineReuseInput(LInstructionHelper<1, Ops, Temps>* lir, MDefinition* mir, uint32_t operand);
 
+    template <size_t Ops, size_t Temps>
+    inline void defineInt64ReuseInput(LInstructionHelper<INT64_PIECES, Ops, Temps>* lir,
+                                      MDefinition* mir, uint32_t operand);
+
     // Returns a box allocation for a Value-typed instruction.
     inline LBoxAllocation useBox(MDefinition* mir, LUse::Policy policy = LUse::REGISTER,
                                  bool useAtStart = false);
 
     // Returns a box allocation. The use is either typed, a Value, or
     // a constant (if useConstant is true).
     inline LBoxAllocation useBoxOrTypedOrConstant(MDefinition* mir, bool useConstant);
 
     // Returns an int64 allocation for an Int64-typed instruction.
     inline LInt64Allocation useInt64(MDefinition* mir, LUse::Policy policy, bool useAtStart);
-    inline LInt64Allocation useInt64(MDefinition* mir);
-    inline LInt64Allocation useInt64OrConstant(MDefinition* mir);
+    inline LInt64Allocation useInt64(MDefinition* mir, bool useAtStart = false);
+    inline LInt64Allocation useInt64OrConstant(MDefinition* mir, bool useAtStart = false);
     inline LInt64Allocation useInt64Register(MDefinition* mir, bool useAtStart = false);
 
+    LInt64Allocation useInt64RegisterAtStart(MDefinition* mir) {
+        return useInt64Register(mir, /* useAtStart = */ true);
+    }
+    LInt64Allocation useInt64OrConstantAtStart(MDefinition* mir) {
+        return useInt64OrConstant(mir, /* useAtStart = */ true);
+    }
+
     // Rather than defining a new virtual register, sets |ins| to have the same
     // virtual register as |as|.
     inline void redefine(MDefinition* ins, MDefinition* as);
 
     // Redefine a sin/cos call to sincos.
     inline void redefine(MDefinition* def, MDefinition* as, MMathFunction::Function func);
 
     TempAllocator& alloc() const {
--- a/js/src/jit/x64/Assembler-x64.h
+++ b/js/src/jit/x64/Assembler-x64.h
@@ -557,16 +557,25 @@ class Assembler : public AssemblerX86Sha
         masm.shlq_ir(imm.value, dest.encoding());
     }
     void shrq(Imm32 imm, Register dest) {
         masm.shrq_ir(imm.value, dest.encoding());
     }
     void sarq(Imm32 imm, Register dest) {
         masm.sarq_ir(imm.value, dest.encoding());
     }
+    void shlq_cl(Register dest) {
+        masm.shlq_CLr(dest.encoding());
+    }
+    void shrq_cl(Register dest) {
+        masm.shrq_CLr(dest.encoding());
+    }
+    void sarq_cl(Register dest) {
+        masm.sarq_CLr(dest.encoding());
+    }
     void orq(Imm32 imm, Register dest) {
         masm.orq_ir(imm.value, dest.encoding());
     }
     void orq(Register src, Register dest) {
         masm.orq_rr(src.encoding(), dest.encoding());
     }
     void orq(const Operand& src, Register dest) {
         switch (src.kind()) {
@@ -584,16 +593,31 @@ class Assembler : public AssemblerX86Sha
         }
     }
     void xorq(Register src, Register dest) {
         masm.xorq_rr(src.encoding(), dest.encoding());
     }
     void xorq(Imm32 imm, Register dest) {
         masm.xorq_ir(imm.value, dest.encoding());
     }
+    void xorq(const Operand& src, Register dest) {
+        switch (src.kind()) {
+          case Operand::REG:
+            masm.xorq_rr(src.reg(), dest.encoding());
+            break;
+          case Operand::MEM_REG_DISP:
+            masm.xorq_mr(src.disp(), src.base(), dest.encoding());
+            break;
+          case Operand::MEM_ADDRESS32:
+            masm.xorq_mr(src.address(), dest.encoding());
+            break;
+          default:
+            MOZ_CRASH("unexpected operand kind");
+        }
+    }
 
     void imulq(Register src, Register dest) {
         masm.imulq_rr(src.encoding(), dest.encoding());
     }
     void vcvtsi2sdq(Register src, FloatRegister dest) {
         masm.vcvtsi2sdq_rr(src.encoding(), dest.encoding());
     }
 
--- a/js/src/jit/x64/BaseAssembler-x64.h
+++ b/js/src/jit/x64/BaseAssembler-x64.h
@@ -108,16 +108,28 @@ class BaseAssemblerX64 : public BaseAsse
     }
 
     void orq_mr(const void* addr, RegisterID dst)
     {
         spew("orq        %p, %s", addr, GPReg64Name(dst));
         m_formatter.oneByteOp64(OP_OR_GvEv, addr, dst);
     }
 
+    void xorq_mr(int32_t offset, RegisterID base, RegisterID dst)
+    {
+        spew("xorq       " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(dst));
+        m_formatter.oneByteOp64(OP_XOR_GvEv, offset, base, dst);
+    }
+
+    void xorq_mr(const void* addr, RegisterID dst)
+    {
+        spew("xorq       %p, %s", addr, GPReg64Name(dst));
+        m_formatter.oneByteOp64(OP_XOR_GvEv, addr, dst);
+    }
+
     void andq_ir(int32_t imm, RegisterID dst)
     {
         spew("andq       $0x%" PRIx64 ", %s", int64_t(imm), GPReg64Name(dst));
         if (CAN_SIGN_EXTEND_8_32(imm)) {
             m_formatter.oneByteOp64(OP_GROUP1_EvIb, dst, GROUP1_OP_AND);
             m_formatter.immediate8s(imm);
         } else {
             if (dst == rax)
@@ -222,16 +234,28 @@ class BaseAssemblerX64 : public BaseAsse
     }
 
     void sarq_CLr(RegisterID dst)
     {
         spew("sarq       %%cl, %s", GPReg64Name(dst));
         m_formatter.oneByteOp64(OP_GROUP2_EvCL, dst, GROUP2_OP_SAR);
     }
 
+    void shlq_CLr(RegisterID dst)
+    {
+        spew("shlq       %%cl, %s", GPReg64Name(dst));
+        m_formatter.oneByteOp64(OP_GROUP2_EvCL, dst, GROUP2_OP_SHL);
+    }
+
+    void shrq_CLr(RegisterID dst)
+    {
+        spew("shrq       %%cl, %s", GPReg64Name(dst));
+        m_formatter.oneByteOp64(OP_GROUP2_EvCL, dst, GROUP2_OP_SHR);
+    }
+
     void sarq_ir(int32_t imm, RegisterID dst)
     {
         MOZ_ASSERT(imm < 64);
         spew("sarq       $%d, %s", imm, GPReg64Name(dst));
         if (imm == 1)
             m_formatter.oneByteOp64(OP_GROUP2_Ev1, dst, GROUP2_OP_SAR);
         else {
             m_formatter.oneByteOp64(OP_GROUP2_EvIb, dst, GROUP2_OP_SAR);
--- a/js/src/jit/x64/CodeGenerator-x64.cpp
+++ b/js/src/jit/x64/CodeGenerator-x64.cpp
@@ -245,16 +245,88 @@ CodeGeneratorX64::visitCompare64AndBranc
     else
         masm.cmpPtr(lhs, ToOperand(rhs));
 
     bool isSigned = mir->compareType() == MCompare::Compare_Int64;
     emitBranch(JSOpToCondition(lir->jsop(), isSigned), lir->ifTrue(), lir->ifFalse());
 }
 
 void
+CodeGeneratorX64::visitBitOpI64(LBitOpI64* lir)
+{
+    Register lhs = ToRegister(lir->getOperand(0));
+    const LAllocation* rhs = lir->getOperand(1);
+
+    switch (lir->bitop()) {
+      case JSOP_BITOR:
+        if (rhs->isConstant())
+            masm.or64(Imm64(ToInt64(rhs)), Register64(lhs));
+        else
+            masm.orq(ToOperand(rhs), lhs);
+        break;
+      case JSOP_BITXOR:
+        if (rhs->isConstant())
+            masm.xor64(Imm64(ToInt64(rhs)), Register64(lhs));
+        else
+            masm.xorq(ToOperand(rhs), lhs);
+        break;
+      case JSOP_BITAND:
+        if (rhs->isConstant())
+            masm.and64(Imm64(ToInt64(rhs)), Register64(lhs));
+        else
+            masm.andq(ToOperand(rhs), lhs);
+        break;
+      default:
+        MOZ_CRASH("unexpected binary opcode");
+    }
+}
+
+void
+CodeGeneratorX64::visitShiftI64(LShiftI64* lir)
+{
+    Register lhs = ToRegister(lir->getOperand(0));
+    const LAllocation* rhs = lir->getOperand(1);
+
+    if (rhs->isConstant()) {
+        int32_t shift = ToInt32(rhs) & 0x3F;
+        switch (lir->bitop()) {
+          case JSOP_LSH:
+            if (shift)
+                masm.shlq(Imm32(shift), lhs);
+            break;
+          case JSOP_RSH:
+            if (shift)
+                masm.sarq(Imm32(shift), lhs);
+            break;
+          case JSOP_URSH:
+            if (shift)
+                masm.shrq(Imm32(shift), lhs);
+            break;
+          default:
+            MOZ_CRASH("Unexpected shift op");
+        }
+    } else {
+        MOZ_ASSERT(ToRegister(rhs) == ecx);
+        switch (lir->bitop()) {
+          case JSOP_LSH:
+            masm.shlq_cl(lhs);
+            break;
+          case JSOP_RSH:
+            masm.sarq_cl(lhs);
+            break;
+          case JSOP_URSH:
+            masm.shrq_cl(lhs);
+            break;
+          default:
+            MOZ_CRASH("Unexpected shift op");
+        }
+    }
+}
+
+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
@@ -39,16 +39,18 @@ class CodeGeneratorX64 : public CodeGene
     void visitBox(LBox* box);
     void visitUnbox(LUnbox* unbox);
     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 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/MacroAssembler-x64-inl.h
+++ b/js/src/jit/x64/MacroAssembler-x64-inl.h
@@ -39,18 +39,47 @@ void
 MacroAssembler::andPtr(Imm32 imm, Register dest)
 {
     andq(imm, dest);
 }
 
 void
 MacroAssembler::and64(Imm64 imm, Register64 dest)
 {
-    movq(ImmWord(uintptr_t(imm.value)), ScratchReg);
-    andq(ScratchReg, dest.reg);
+    if (INT32_MIN <= int64_t(imm.value) && int64_t(imm.value) <= INT32_MAX) {
+        andq(Imm32(imm.value), dest.reg);
+    } else {
+        ScratchRegisterScope scratch(*this);
+        movq(ImmWord(uintptr_t(imm.value)), scratch);
+        andq(scratch, dest.reg);
+    }
+}
+
+void
+MacroAssembler::or64(Imm64 imm, Register64 dest)
+{
+    if (INT32_MIN <= int64_t(imm.value) && int64_t(imm.value) <= INT32_MAX) {
+        orq(Imm32(imm.value), dest.reg);
+    } else {
+        ScratchRegisterScope scratch(*this);
+        movq(ImmWord(uintptr_t(imm.value)), scratch);
+        orq(scratch, dest.reg);
+    }
+}
+
+void
+MacroAssembler::xor64(Imm64 imm, Register64 dest)
+{
+    if (INT32_MIN <= int64_t(imm.value) && int64_t(imm.value) <= INT32_MAX) {
+        xorq(Imm32(imm.value), dest.reg);
+    } else {
+        ScratchRegisterScope scratch(*this);
+        movq(ImmWord(uintptr_t(imm.value)), scratch);
+        xorq(scratch, dest.reg);
+    }
 }
 
 void
 MacroAssembler::orPtr(Register src, Register dest)
 {
     orq(src, dest);
 }
 
--- a/js/src/jit/x86-shared/Lowering-x86-shared.cpp
+++ b/js/src/jit/x86-shared/Lowering-x86-shared.cpp
@@ -76,32 +76,66 @@ LIRGeneratorX86Shared::lowerForShift(LIn
         ins->setOperand(1, useOrConstantAtStart(rhs));
     else
         ins->setOperand(1, lhs != rhs ? useFixed(rhs, ecx) : useFixedAtStart(rhs, ecx));
 
     defineReuseInput(ins, mir, 0);
 }
 
 void
+LIRGeneratorX86Shared::lowerForShiftInt64(LInstructionHelper<INT64_PIECES, INT64_PIECES + 1, 0>* ins,
+                                          MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
+{
+    ins->setInt64Operand(0, useInt64RegisterAtStart(lhs));
+
+    // shift operator should be constant or in register ecx
+    // x86 can't shift a non-ecx register
+    if (rhs->isConstant()) {
+        ins->setOperand(INT64_PIECES, useOrConstantAtStart(rhs));
+    } else {
+        // The operands are int64, but we only care about the lower 32 bits of
+        // the RHS. On 32-bit, the code below will load that part in ecx and
+        // will discard the upper half.
+        ensureDefined(rhs);
+        bool useAtStart = (lhs == rhs);
+        LUse use(ecx, useAtStart);
+        use.setVirtualRegister(rhs->virtualRegister());
+        ins->setOperand(INT64_PIECES, use);
+    }
+
+    defineInt64ReuseInput(ins, mir, 0);
+}
+
+void
 LIRGeneratorX86Shared::lowerForALU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir,
                                    MDefinition* input)
 {
     ins->setOperand(0, useRegisterAtStart(input));
     defineReuseInput(ins, mir, 0);
 }
 
 void
 LIRGeneratorX86Shared::lowerForALU(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir,
                                    MDefinition* lhs, MDefinition* rhs)
 {
     ins->setOperand(0, useRegisterAtStart(lhs));
     ins->setOperand(1, lhs != rhs ? useOrConstant(rhs) : useOrConstantAtStart(rhs));
     defineReuseInput(ins, mir, 0);
 }
 
+void
+LIRGeneratorX86Shared::lowerForALUInt64(LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>* ins,
+                                        MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
+{
+    ins->setInt64Operand(0, useInt64RegisterAtStart(lhs));
+    ins->setInt64Operand(INT64_PIECES,
+                         lhs != rhs ? useInt64OrConstant(rhs) : useInt64OrConstantAtStart(rhs));
+    defineInt64ReuseInput(ins, mir, 0);
+}
+
 template<size_t Temps>
 void
 LIRGeneratorX86Shared::lowerForFPU(LInstructionHelper<1, 2, Temps>* ins, MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
 {
     // Without AVX, we'll need to use the x86 encodings where one of the
     // inputs must be the same location as the output.
     //
     // :TODO: (Bug 1132894) Note, we might have to allocate a different
--- a/js/src/jit/x86-shared/Lowering-x86-shared.h
+++ b/js/src/jit/x86-shared/Lowering-x86-shared.h
@@ -26,16 +26,22 @@ class LIRGeneratorX86Shared : public LIR
     void visitGuardShape(MGuardShape* ins);
     void visitGuardObjectGroup(MGuardObjectGroup* ins);
     void visitPowHalf(MPowHalf* ins);
     void lowerForShift(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir, MDefinition* lhs,
                        MDefinition* rhs);
     void lowerForALU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir, MDefinition* input);
     void lowerForALU(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir, MDefinition* lhs,
                      MDefinition* rhs);
+
+    void lowerForALUInt64(LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>* ins, MDefinition* mir,
+                          MDefinition* lhs, MDefinition* rhs);
+    void lowerForShiftInt64(LInstructionHelper<INT64_PIECES, INT64_PIECES + 1, 0>* ins,
+                            MDefinition* mir, MDefinition* lhs, MDefinition* rhs);
+
     template<size_t Temps>
     void lowerForFPU(LInstructionHelper<1, 2, Temps>* ins, MDefinition* mir, MDefinition* lhs,
                      MDefinition* rhs);
     void lowerForCompIx4(LSimdBinaryCompIx4* ins, MSimdBinaryComp* mir,
                          MDefinition* lhs, MDefinition* rhs);
     void lowerForCompFx4(LSimdBinaryCompFx4* ins, MSimdBinaryComp* mir,
                          MDefinition* lhs, MDefinition* rhs);
     void lowerForBitAndAndBranch(LBitAndAndBranch* baab, MInstruction* mir,
--- a/js/src/jit/x86/MacroAssembler-x86-inl.h
+++ b/js/src/jit/x86/MacroAssembler-x86-inl.h
@@ -48,16 +48,30 @@ MacroAssembler::andPtr(Imm32 imm, Regist
 void
 MacroAssembler::and64(Imm64 imm, Register64 dest)
 {
     andl(Imm32(imm.value & 0xFFFFFFFFL), dest.low);
     andl(Imm32((imm.value >> 32) & 0xFFFFFFFFL), dest.high);
 }
 
 void
+MacroAssembler::or64(Imm64 imm, Register64 dest)
+{
+    orl(Imm32(imm.value & 0xFFFFFFFFL), dest.low);
+    orl(Imm32((imm.value >> 32) & 0xFFFFFFFFL), dest.high);
+}
+
+void
+MacroAssembler::xor64(Imm64 imm, Register64 dest)
+{
+    xorl(Imm32(imm.value & 0xFFFFFFFFL), dest.low);
+    xorl(Imm32((imm.value >> 32) & 0xFFFFFFFFL), dest.high);
+}
+
+void
 MacroAssembler::orPtr(Register src, Register dest)
 {
     orl(src, dest);
 }
 
 void
 MacroAssembler::orPtr(Imm32 imm, Register dest)
 {