Bug 1249601 - Implement wasm i64 comparisons. r=sunfish
authorJan de Mooij <jdemooij@mozilla.com>
Thu, 25 Feb 2016 14:59:01 +0100
changeset 321909 c167859aa1f5a4d8c3ce76c5d841eabdbce59241
parent 321908 aa348994df48dd1ab2216b9f13473c6a6049ec9f
child 321910 6f7d0257dc2e17e5a6142e76e68b9ec0b7e9c060
push id5913
push userjlund@mozilla.com
push dateMon, 25 Apr 2016 16:57:49 +0000
treeherdermozilla-beta@dcaf0a6fa115 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssunfish
bugs1249601
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 1249601 - Implement wasm i64 comparisons. r=sunfish
js/src/asmjs/Wasm.cpp
js/src/asmjs/WasmIonCompile.cpp
js/src/jit-test/tests/wasm/basic-integer.js
js/src/jit/LIR.h
js/src/jit/Lowering.cpp
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jit/shared/CodeGenerator-shared-inl.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/CodeGenerator-x64.cpp
js/src/jit/x64/CodeGenerator-x64.h
--- a/js/src/asmjs/Wasm.cpp
+++ b/js/src/asmjs/Wasm.cpp
@@ -520,18 +520,17 @@ DecodeExpr(FunctionDecoder& f, ExprType 
       case Expr::I64LtS:
       case Expr::I64LtU:
       case Expr::I64LeS:
       case Expr::I64LeU:
       case Expr::I64GtS:
       case Expr::I64GtU:
       case Expr::I64GeS:
       case Expr::I64GeU:
-        return f.fail("NYI: i64") &&
-               DecodeComparisonOperator(f, expected, ExprType::I64);
+        return DecodeComparisonOperator(f, expected, ExprType::I64);
       case Expr::F32Eq:
       case Expr::F32Ne:
       case Expr::F32Lt:
       case Expr::F32Le:
       case Expr::F32Gt:
       case Expr::F32Ge:
         return DecodeComparisonOperator(f, expected, ExprType::F32);
       case Expr::F64Eq:
--- a/js/src/asmjs/WasmIonCompile.cpp
+++ b/js/src/asmjs/WasmIonCompile.cpp
@@ -2221,16 +2221,40 @@ EmitComparison(FunctionCompiler& f, Expr
           case Expr::I32LeS: case Expr::I32LtS: case Expr::I32GeS: case Expr::I32GtS:
           case Expr::I32Eq: case Expr::I32Ne:
             compareType = MCompare::Compare_Int32; break;
           case Expr::I32GeU: case Expr::I32GtU: case Expr::I32LeU: case Expr::I32LtU:
             compareType = MCompare::Compare_UInt32; break;
           default: MOZ_CRASH("impossibru opcode");
         }
         break;
+      case Expr::I64Eq:
+      case Expr::I64Ne:
+      case Expr::I64LeS:
+      case Expr::I64LtS:
+      case Expr::I64LeU:
+      case Expr::I64LtU:
+      case Expr::I64GeS:
+      case Expr::I64GtS:
+      case Expr::I64GeU:
+      case Expr::I64GtU:
+        if (!EmitExpr(f, ExprType::I64, &lhs) || !EmitExpr(f, ExprType::I64, &rhs))
+            return false;
+        switch (expr) {
+          case Expr::I64LeS: case Expr::I64LtS: case Expr::I64GeS: case Expr::I64GtS:
+          case Expr::I64Eq: case Expr::I64Ne:
+            compareType = MCompare::Compare_Int64;
+            break;
+          case Expr::I64GeU: case Expr::I64GtU: case Expr::I64LeU: case Expr::I64LtU:
+            compareType = MCompare::Compare_UInt64;
+            break;
+          default:
+            MOZ_CRASH("unexpected opcode");
+        }
+        break;
       case Expr::F32Eq:
       case Expr::F32Ne:
       case Expr::F32Le:
       case Expr::F32Lt:
       case Expr::F32Ge:
       case Expr::F32Gt:
         if (!EmitExpr(f, ExprType::F32, &lhs) || !EmitExpr(f, ExprType::F32, &rhs))
             return false;
@@ -2247,45 +2271,55 @@ EmitComparison(FunctionCompiler& f, Expr
         compareType = MCompare::Compare_Double;
         break;
       default: MOZ_CRASH("unexpected comparison opcode");
     }
 
     JSOp compareOp;
     switch (expr) {
       case Expr::I32Eq:
+      case Expr::I64Eq:
       case Expr::F32Eq:
       case Expr::F64Eq:
         compareOp = JSOP_EQ;
         break;
       case Expr::I32Ne:
+      case Expr::I64Ne:
       case Expr::F32Ne:
       case Expr::F64Ne:
         compareOp = JSOP_NE;
         break;
       case Expr::I32LeS:
       case Expr::I32LeU:
+      case Expr::I64LeS:
+      case Expr::I64LeU:
       case Expr::F32Le:
       case Expr::F64Le:
         compareOp = JSOP_LE;
         break;
       case Expr::I32LtS:
       case Expr::I32LtU:
+      case Expr::I64LtS:
+      case Expr::I64LtU:
       case Expr::F32Lt:
       case Expr::F64Lt:
         compareOp = JSOP_LT;
         break;
       case Expr::I32GeS:
       case Expr::I32GeU:
+      case Expr::I64GeS:
+      case Expr::I64GeU:
       case Expr::F32Ge:
       case Expr::F64Ge:
         compareOp = JSOP_GE;
         break;
       case Expr::I32GtS:
       case Expr::I32GtU:
+      case Expr::I64GtS:
+      case Expr::I64GtU:
       case Expr::F32Gt:
       case Expr::F64Gt:
         compareOp = JSOP_GT;
         break;
       default: MOZ_CRASH("unexpected comparison opcode");
     }
 
     *def = f.compare(lhs, rhs, compareOp, compareType);
@@ -2820,16 +2854,26 @@ EmitExpr(FunctionCompiler& f, ExprType t
       case Expr::I32LtS:
       case Expr::I32LeS:
       case Expr::I32GtS:
       case Expr::I32GeS:
       case Expr::I32LtU:
       case Expr::I32LeU:
       case Expr::I32GtU:
       case Expr::I32GeU:
+      case Expr::I64Eq:
+      case Expr::I64Ne:
+      case Expr::I64LtS:
+      case Expr::I64LeS:
+      case Expr::I64LtU:
+      case Expr::I64LeU:
+      case Expr::I64GtS:
+      case Expr::I64GeS:
+      case Expr::I64GtU:
+      case Expr::I64GeU:
       case Expr::F32Eq:
       case Expr::F32Ne:
       case Expr::F32Lt:
       case Expr::F32Le:
       case Expr::F32Gt:
       case Expr::F32Ge:
       case Expr::F64Eq:
       case Expr::F64Ne:
@@ -3028,26 +3072,16 @@ EmitExpr(FunctionCompiler& f, ExprType t
       case Expr::I64RemS:
       case Expr::I64RemU:
       case Expr::I64Or:
       case Expr::I64And:
       case Expr::I64Xor:
       case Expr::I64Shl:
       case Expr::I64ShrU:
       case Expr::I64ShrS:
-      case Expr::I64Eq:
-      case Expr::I64Ne:
-      case Expr::I64LtS:
-      case Expr::I64LeS:
-      case Expr::I64LtU:
-      case Expr::I64LeU:
-      case Expr::I64GtS:
-      case Expr::I64GeS:
-      case Expr::I64GtU:
-      case Expr::I64GeU:
         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
@@ -8,17 +8,33 @@ function testUnary(type, opcode, op, exp
   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);
 }
 
 function testComparison(type, opcode, lhs, rhs, expect) {
-  assertEq(wasmEvalText('(module (func (param ' + type + ') (param ' + type + ') (result i32) (' + 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 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)
+                              (if_else (i64.${opcode} (get_local 0) (get_local 1))
+                                (i32.const 1)
+                                (i32.const 0)))
+                            (func (result i32) (call 0 (i64.const ${lhs}) (i64.const ${rhs})))
+                            (export "" 1))`)(), expect);
+  } else {
+    assertEq(wasmEvalText('(module (func (param ' + type + ') (param ' + type + ') (result i32) (' + type + '.' + opcode + ' (get_local 0) (get_local 1))) (export "" 0))')(lhs, rhs), expect);
+  }
 }
 
 testUnary('i32', 'clz', 40, 26);
 //testUnary('i32', 'ctz', 40, 0); // TODO: NYI
 //testUnary('i32', 'popcnt', 40, 0); // TODO: NYI
 
 testBinary('i32', 'add', 40, 2, 42);
 testBinary('i32', 'sub', 40, 2, 38);
@@ -58,26 +74,48 @@ testComparison('i32', 'ge_u', 40, 40, 1)
 //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
 
-//testComparison('i64', 'eq', 40, 40, 1); // TODO: NYI
-//testComparison('i64', 'ne', 40, 40, 0); // TODO: NYI
-//testComparison('i64', 'lt_s', 40, 40, 0); // TODO: NYI
-//testComparison('i64', 'lt_u', 40, 40, 0); // TODO: NYI
-//testComparison('i64', 'le_s', 40, 40, 1); // TODO: NYI
-//testComparison('i64', 'le_u', 40, 40, 1); // TODO: NYI
-//testComparison('i64', 'gt_s', 40, 40, 0); // TODO: NYI
-//testComparison('i64', 'gt_u', 40, 40, 0); // TODO: NYI
-//testComparison('i64', 'ge_s', 40, 40, 1); // TODO: NYI
-//testComparison('i64', 'ge_u', 40, 40, 1); // TODO: NYI
+if (getBuildConfiguration().x64) {
+    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);
+    testComparison('i64', 'ge_s', 40, 40, 1);
+    testComparison('i64', 'ge_u', 40, 40, 1);
+    testComparison('i64', 'eq', "0x400012345678", "0x400012345678", 1);
+    testComparison('i64', 'ne', "0x400012345678", "0x400012345678", 0);
+    testComparison('i64', 'ne', "0x400012345678", "0x500012345678", 1);
+    testComparison('i64', 'eq', "0xffffffffffffffff", "-1", 1);
+    testComparison('i64', 'lt_s', "0x8000000012345678", "0x1", 1);
+    testComparison('i64', 'lt_u', "0x8000000012345678", "0x1", 0);
+    testComparison('i64', 'le_s', "-1", "0", 1);
+    testComparison('i64', 'le_u', "-1", "-1", 1);
+    testComparison('i64', 'gt_s', "1", "0x8000000000000000", 1);
+    testComparison('i64', 'gt_u', "1", "0x8000000000000000", 0);
+    testComparison('i64', 'ge_s', "1", "0x8000000000000000", 1);
+    testComparison('i64', 'ge_u', "1", "0x8000000000000000", 0);
+} else {
+    // Sleeper test: once i64 works on more platforms, remove this if-else.
+    try {
+        testComparison('i64', 'eq', 40, 40, 1);
+        assertEq(0, 1);
+    } catch(e) {
+        assertEq(e.toString().indexOf("NYI on this platform") >= 0, true);
+    }
+}
 
 assertErrorMessage(() => wasmEvalText('(module (func (param f32) (result i32) (i32.clz (get_local 0))))'), TypeError, mismatchError("f32", "i32"));
 assertErrorMessage(() => wasmEvalText('(module (func (param i32) (result f32) (i32.clz (get_local 0))))'), TypeError, mismatchError("i32", "f32"));
 assertErrorMessage(() => wasmEvalText('(module (func (param f32) (result f32) (i32.clz (get_local 0))))'), TypeError, mismatchError("i32", "f32"));
 
 assertErrorMessage(() => wasmEvalText('(module (func (param f32) (param i32) (result i32) (i32.add (get_local 0) (get_local 1))))'), TypeError, mismatchError("f32", "i32"));
 assertErrorMessage(() => wasmEvalText('(module (func (param i32) (param f32) (result i32) (i32.add (get_local 0) (get_local 1))))'), TypeError, mismatchError("f32", "i32"));
 assertErrorMessage(() => wasmEvalText('(module (func (param i32) (param i32) (result f32) (i32.add (get_local 0) (get_local 1))))'), TypeError, mismatchError("i32", "f32"));
--- a/js/src/jit/LIR.h
+++ b/js/src/jit/LIR.h
@@ -1102,16 +1102,24 @@ class LInstructionHelper : public detail
     void setBoxOperand(size_t index, const LBoxAllocation& alloc) {
 #ifdef JS_NUNBOX32
         operands_[index] = alloc.type();
         operands_[index + 1] = alloc.payload();
 #else
         operands_[index] = alloc.value();
 #endif
     }
+    void setInt64Operand(size_t index, const LInt64Allocation& alloc) {
+#if JS_BITS_PER_WORD == 32
+        operands_[index] = alloc.low();
+        operands_[index + 1] = alloc.high();
+#else
+        operands_[index] = alloc.value();
+#endif
+    }
 };
 
 template<size_t Defs, size_t Temps>
 class LVariadicInstruction : public details::LInstructionFixedDefsTempsHelper<Defs, Temps>
 {
     FixedList<LAllocation> operands_;
 
   public:
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -834,16 +834,29 @@ LIRGenerator::visitTest(MTest* test)
             else
                 rhs = useRegister(right);
             LCompareAndBranch* lir = new(alloc()) LCompareAndBranch(comp, op, lhs, rhs,
                                                                     ifTrue, ifFalse);
             add(lir, test);
             return;
         }
 
+        // Compare and branch Int64.
+        if (comp->compareType() == MCompare::Compare_Int64 ||
+            comp->compareType() == MCompare::Compare_UInt64)
+        {
+            JSOp op = ReorderComparison(comp->jsop(), &left, &right);
+            LCompare64AndBranch* lir = new(alloc()) LCompare64AndBranch(comp, op,
+                                                                        useInt64Register(left),
+                                                                        useInt64OrConstant(right),
+                                                                        ifTrue, ifFalse);
+            add(lir, test);
+            return;
+        }
+
         // Compare and branch doubles.
         if (comp->isDoubleComparison()) {
             LAllocation lhs = useRegister(left);
             LAllocation rhs = useRegister(right);
             LCompareDAndBranch* lir = new(alloc()) LCompareDAndBranch(comp, lhs, rhs,
                                                                       ifTrue, ifFalse);
             add(lir, test);
             return;
@@ -1064,16 +1077,26 @@ LIRGenerator::visitCompare(MCompare* com
             rhs = useAnyOrConstant(right);
         } else {
             rhs = useRegister(right);
         }
         define(new(alloc()) LCompare(op, lhs, rhs), comp);
         return;
     }
 
+    // Compare Int64.
+    if (comp->compareType() == MCompare::Compare_Int64 ||
+        comp->compareType() == MCompare::Compare_UInt64)
+    {
+        JSOp op = ReorderComparison(comp->jsop(), &left, &right);
+        define(new(alloc()) LCompare64(op, useInt64Register(left), useInt64OrConstant(right)),
+               comp);
+        return;
+    }
+
     // Compare doubles.
     if (comp->isDoubleComparison()) {
         define(new(alloc()) LCompareD(useRegister(left), useRegister(right)), comp);
         return;
     }
 
     // Compare float32.
     if (comp->isFloat32Comparison()) {
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -1803,16 +1803,17 @@ MCompare::New(TempAllocator& alloc, MDef
     return new(alloc) MCompare(left, right, op);
 }
 
 MCompare*
 MCompare::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right, JSOp op,
                    CompareType compareType)
 {
     MOZ_ASSERT(compareType == Compare_Int32 || compareType == Compare_UInt32 ||
+               compareType == Compare_Int64 || compareType == Compare_UInt64 ||
                compareType == Compare_Double || compareType == Compare_Float32);
     MCompare* comp = new(alloc) MCompare(left, right, op);
     comp->compareType_ = compareType;
     comp->operandMightEmulateUndefined_ = false;
     comp->setResultType(MIRType_Int32);
     return comp;
 }
 
@@ -3940,16 +3941,31 @@ MCompare::tryFold(bool* result)
             return true;
         }
         return false;
     }
 
     return false;
 }
 
+template <typename T>
+static bool
+FoldComparison(JSOp op, T left, T right)
+{
+    switch (op) {
+      case JSOP_LT: return left < right;
+      case JSOP_LE: return left <= right;
+      case JSOP_GT: return left > right;
+      case JSOP_GE: return left >= right;
+      case JSOP_STRICTEQ: case JSOP_EQ: return left == right;
+      case JSOP_STRICTNE: case JSOP_NE: return left != right;
+      default: MOZ_CRASH("Unexpected op.");
+    }
+}
+
 bool
 MCompare::evaluateConstantOperands(TempAllocator& alloc, bool* result)
 {
     if (type() != MIRType_Boolean && type() != MIRType_Int32)
         return false;
 
     MDefinition* left = getOperand(0);
     MDefinition* right = getOperand(1);
@@ -4041,106 +4057,41 @@ MCompare::evaluateConstantOperands(TempA
     MConstant* lhs = left->toConstant();
     MConstant* rhs = right->toConstant();
 
     // Fold away some String equality comparisons.
     if (lhs->type() == MIRType_String && rhs->type() == MIRType_String) {
         int32_t comp = 0; // Default to equal.
         if (left != right)
             comp = CompareAtoms(&lhs->toString()->asAtom(), &rhs->toString()->asAtom());
-
-        switch (jsop_) {
-          case JSOP_LT:
-            *result = (comp < 0);
-            break;
-          case JSOP_LE:
-            *result = (comp <= 0);
-            break;
-          case JSOP_GT:
-            *result = (comp > 0);
-            break;
-          case JSOP_GE:
-            *result = (comp >= 0);
-            break;
-          case JSOP_STRICTEQ: // Fall through.
-          case JSOP_EQ:
-            *result = (comp == 0);
-            break;
-          case JSOP_STRICTNE: // Fall through.
-          case JSOP_NE:
-            *result = (comp != 0);
-            break;
-          default:
-            MOZ_CRASH("Unexpected op.");
-        }
-
+        *result = FoldComparison(jsop_, comp, 0);
         return true;
     }
 
     if (compareType_ == Compare_UInt32) {
-        uint32_t lhsUint = uint32_t(lhs->toInt32());
-        uint32_t rhsUint = uint32_t(rhs->toInt32());
-
-        switch (jsop_) {
-          case JSOP_LT:
-            *result = (lhsUint < rhsUint);
-            break;
-          case JSOP_LE:
-            *result = (lhsUint <= rhsUint);
-            break;
-          case JSOP_GT:
-            *result = (lhsUint > rhsUint);
-            break;
-          case JSOP_GE:
-            *result = (lhsUint >= rhsUint);
-            break;
-          case JSOP_STRICTEQ: // Fall through.
-          case JSOP_EQ:
-            *result = (lhsUint == rhsUint);
-            break;
-          case JSOP_STRICTNE: // Fall through.
-          case JSOP_NE:
-            *result = (lhsUint != rhsUint);
-            break;
-          default:
-            MOZ_CRASH("Unexpected op.");
-        }
-
+        *result = FoldComparison(jsop_, uint32_t(lhs->toInt32()), uint32_t(rhs->toInt32()));
+        return true;
+    }
+
+    if (compareType_ == Compare_Int64) {
+        *result = FoldComparison(jsop_, lhs->toInt64(), rhs->toInt64());
         return true;
     }
 
-    if (!lhs->isTypeRepresentableAsDouble() || !rhs->isTypeRepresentableAsDouble())
-        return false;
-
-    switch (jsop_) {
-      case JSOP_LT:
-        *result = (lhs->numberToDouble() < rhs->numberToDouble());
-        break;
-      case JSOP_LE:
-        *result = (lhs->numberToDouble() <= rhs->numberToDouble());
-        break;
-      case JSOP_GT:
-        *result = (lhs->numberToDouble() > rhs->numberToDouble());
-        break;
-      case JSOP_GE:
-        *result = (lhs->numberToDouble() >= rhs->numberToDouble());
-        break;
-      case JSOP_STRICTEQ: // Fall through.
-      case JSOP_EQ:
-        *result = (lhs->numberToDouble() == rhs->numberToDouble());
-        break;
-      case JSOP_STRICTNE: // Fall through.
-      case JSOP_NE:
-        *result = (lhs->numberToDouble() != rhs->numberToDouble());
-        break;
-      default:
-        return false;
-    }
-
-    return true;
+    if (compareType_ == Compare_UInt64) {
+        *result = FoldComparison(jsop_, uint64_t(lhs->toInt64()), uint64_t(rhs->toInt64()));
+        return true;
+    }
+
+    if (lhs->isTypeRepresentableAsDouble() && rhs->isTypeRepresentableAsDouble()) {
+        *result = FoldComparison(jsop_, lhs->numberToDouble(), rhs->numberToDouble());
+        return true;
+    }
+
+    return false;
 }
 
 MDefinition*
 MCompare::foldsTo(TempAllocator& alloc)
 {
     bool result;
 
     if (tryFold(&result) || evaluateConstantOperands(alloc, &result)) {
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -4360,16 +4360,22 @@ class MCompare
         Compare_Int32,
         Compare_Int32MaybeCoerceBoth,
         Compare_Int32MaybeCoerceLHS,
         Compare_Int32MaybeCoerceRHS,
 
         // Int32 compared as unsigneds
         Compare_UInt32,
 
+        // Int64 compared to Int64.
+        Compare_Int64,
+
+        // Int64 compared as unsigneds.
+        Compare_UInt64,
+
         // Double compared to Double
         Compare_Double,
 
         Compare_DoubleMaybeCoerceLHS,
         Compare_DoubleMaybeCoerceRHS,
 
         // Float compared to Float
         Compare_Float32,
--- a/js/src/jit/shared/CodeGenerator-shared-inl.h
+++ b/js/src/jit/shared/CodeGenerator-shared-inl.h
@@ -20,16 +20,26 @@ ToInt32(const LAllocation* a)
 {
     if (a->isConstantValue())
         return a->toConstant()->toInt32();
     if (a->isConstantIndex())
         return a->toConstantIndex()->index();
     MOZ_CRASH("this is not a constant!");
 }
 
+static inline int64_t
+ToInt64(const LAllocation* a)
+{
+    if (a->isConstantValue())
+        return a->toConstant()->toInt64();
+    if (a->isConstantIndex())
+        return a->toConstantIndex()->index();
+    MOZ_CRASH("this is not a constant!");
+}
+
 static inline double
 ToDouble(const LAllocation* a)
 {
     return a->toConstant()->numberToDouble();
 }
 
 static inline Register
 ToRegister(const LAllocation& a)
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -2257,16 +2257,86 @@ class LCompare : public LInstructionHelp
     MCompare* mir() {
         return mir_->toCompare();
     }
     const char* extraName() const {
         return CodeName[jsop_];
     }
 };
 
+class LCompare64 : public LInstructionHelper<1, 2 * INT64_PIECES, 0>
+{
+    JSOp jsop_;
+
+  public:
+    LIR_HEADER(Compare64)
+
+    static const size_t Lhs = 0;
+    static const size_t Rhs = INT64_PIECES;
+
+    LCompare64(JSOp jsop, const LInt64Allocation& left, const LInt64Allocation& right)
+      : jsop_(jsop)
+    {
+        setInt64Operand(Lhs, left);
+        setInt64Operand(Rhs, right);
+    }
+
+    JSOp jsop() const {
+        return jsop_;
+    }
+    MCompare* mir() {
+        return mir_->toCompare();
+    }
+    const char* extraName() const {
+        return CodeName[jsop_];
+    }
+};
+
+class LCompare64AndBranch : public LControlInstructionHelper<2, 2 * INT64_PIECES, 0>
+{
+    MCompare* cmpMir_;
+    JSOp jsop_;
+
+  public:
+    LIR_HEADER(Compare64AndBranch)
+
+    static const size_t Lhs = 0;
+    static const size_t Rhs = INT64_PIECES;
+
+    LCompare64AndBranch(MCompare* cmpMir, JSOp jsop,
+                        const LInt64Allocation& left, const LInt64Allocation& right,
+                        MBasicBlock* ifTrue, MBasicBlock* ifFalse)
+      : cmpMir_(cmpMir), jsop_(jsop)
+    {
+        setInt64Operand(Lhs, left);
+        setInt64Operand(Rhs, right);
+        setSuccessor(0, ifTrue);
+        setSuccessor(1, ifFalse);
+    }
+
+    JSOp jsop() const {
+        return jsop_;
+    }
+    MBasicBlock* ifTrue() const {
+        return getSuccessor(0);
+    }
+    MBasicBlock* ifFalse() const {
+        return getSuccessor(1);
+    }
+    MTest* mir() const {
+        return mir_->toTest();
+    }
+    MCompare* cmpMir() const {
+        return cmpMir_;
+    }
+    const char* extraName() const {
+        return CodeName[jsop_];
+    }
+};
+
 // Compares two integral values of the same JS type, either integer or object.
 // For objects, both operands are in registers.
 class LCompareAndBranch : public LControlInstructionHelper<2, 2, 0>
 {
     MCompare* cmpMir_;
     JSOp jsop_;
 
   public:
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -107,16 +107,18 @@
     _(TestDAndBranch)               \
     _(TestFAndBranch)               \
     _(TestVAndBranch)               \
     _(TestOAndBranch)               \
     _(FunctionDispatch)             \
     _(ObjectGroupDispatch)          \
     _(Compare)                      \
     _(CompareAndBranch)             \
+    _(Compare64)                    \
+    _(Compare64AndBranch)           \
     _(CompareD)                     \
     _(CompareDAndBranch)            \
     _(CompareF)                     \
     _(CompareFAndBranch)            \
     _(CompareS)                     \
     _(CompareStrictS)               \
     _(CompareB)                     \
     _(CompareBAndBranch)            \
--- a/js/src/jit/shared/Lowering-shared-inl.h
+++ b/js/src/jit/shared/Lowering-shared-inl.h
@@ -659,12 +659,58 @@ LIRGeneratorShared::useBoxOrTypedOrConst
 
 #if defined(JS_NUNBOX32)
     return LBoxAllocation(useRegister(mir), LAllocation());
 #else
     return LBoxAllocation(useRegister(mir));
 #endif
 }
 
+LInt64Allocation
+LIRGeneratorShared::useInt64(MDefinition* mir, LUse::Policy policy, bool useAtStart)
+{
+    MOZ_ASSERT(mir->type() == MIRType_Int64);
+
+    ensureDefined(mir);
+
+    uint32_t vreg = mir->virtualRegister();
+#if JS_BITS_PER_WORD == 32
+    return LInt64Allocation(LUse(vreg + 1, policy, useAtStart),
+                            LUse(vreg, policy, useAtStart));
+#else
+    return LInt64Allocation(LUse(vreg, policy, useAtStart));
+#endif
+}
+
+LInt64Allocation
+LIRGeneratorShared::useInt64(MDefinition* mir)
+{
+    // On 32-bit platforms, always load the value in registers.
+#if JS_BITS_PER_WORD == 32
+    return useInt64(mir, LUse::REGISTER, /* useAtStart = */ false);
+#else
+    return useInt64(mir, LUse::ANY, /* useAtStart = */ false);
+#endif
+}
+
+LInt64Allocation
+LIRGeneratorShared::useInt64Register(MDefinition* mir, bool useAtStart)
+{
+    return useInt64(mir, LUse::REGISTER, useAtStart);
+}
+
+LInt64Allocation
+LIRGeneratorShared::useInt64OrConstant(MDefinition* mir)
+{
+    if (mir->isConstant()) {
+#if defined(JS_NUNBOX32)
+        return LInt64Allocation(LAllocation(mir->toConstant()), LAllocation());
+#else
+        return LInt64Allocation(LAllocation(mir->toConstant()));
+#endif
+    }
+    return useInt64(mir);
+}
+
 } // 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
@@ -166,16 +166,22 @@ class LIRGeneratorShared : public MDefin
     // 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 useInt64Register(MDefinition* mir, bool useAtStart = false);
+
     // 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/CodeGenerator-x64.cpp
+++ b/js/src/jit/x64/CodeGenerator-x64.cpp
@@ -207,16 +207,54 @@ CodeGeneratorX64::visitCompareBitwiseAnd
     MOZ_ASSERT(mir->jsop() == JSOP_EQ || mir->jsop() == JSOP_STRICTEQ ||
                mir->jsop() == JSOP_NE || mir->jsop() == JSOP_STRICTNE);
 
     masm.cmpPtr(lhs.valueReg(), rhs.valueReg());
     emitBranch(JSOpToCondition(mir->compareType(), mir->jsop()), lir->ifTrue(), lir->ifFalse());
 }
 
 void
+CodeGeneratorX64::visitCompare64(LCompare64* lir)
+{
+    MCompare* mir = lir->mir();
+    MOZ_ASSERT(mir->compareType() == MCompare::Compare_Int64 ||
+               mir->compareType() == MCompare::Compare_UInt64);
+
+    Register lhs = ToRegister(lir->getOperand(0));
+    const LAllocation* rhs = lir->getOperand(1);
+
+    if (rhs->isConstant())
+        masm.cmpPtr(lhs, ImmWord(ToInt64(rhs)));
+    else
+        masm.cmpPtr(lhs, ToOperand(rhs));
+
+    bool isSigned = mir->compareType() == MCompare::Compare_Int64;
+    masm.emitSet(JSOpToCondition(lir->jsop(), isSigned), ToRegister(lir->output()));
+}
+
+void
+CodeGeneratorX64::visitCompare64AndBranch(LCompare64AndBranch* lir)
+{
+    MCompare* mir = lir->cmpMir();
+    MOZ_ASSERT(mir->compareType() == MCompare::Compare_Int64 ||
+               mir->compareType() == MCompare::Compare_UInt64);
+
+    Register lhs = ToRegister(lir->getOperand(0));
+    const LAllocation* rhs = lir->getOperand(1);
+
+    if (rhs->isConstant())
+        masm.cmpPtr(lhs, ImmWord(ToInt64(rhs)));
+    else
+        masm.cmpPtr(lhs, ToOperand(rhs));
+
+    bool isSigned = mir->compareType() == MCompare::Compare_Int64;
+    emitBranch(JSOpToCondition(lir->jsop(), isSigned), lir->ifTrue(), lir->ifFalse());
+}
+
+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
@@ -37,16 +37,18 @@ class CodeGeneratorX64 : public CodeGene
   public:
     void visitValue(LValue* value);
     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 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);