Bug 1271972: wasm: Implement i64.ctz, i64.clz, i64.popcount; r=sunfish
authorBenjamin Bouvier <benj@benj.me>
Wed, 11 May 2016 19:00:53 +0200
changeset 297293 4daee069692632e5d4477a0c5710d835da1feed9
parent 297292 906763ccdf9a5c8a4950a48d28be7231d5090eb2
child 297294 8178f7c2581b263e1deacfa9f8ff425f2a872813
push id76682
push userbbouvier@mozilla.com
push dateFri, 13 May 2016 07:38:51 +0000
treeherdermozilla-inbound@df7f2d705931 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssunfish
bugs1271972
milestone49.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 1271972: wasm: Implement i64.ctz, i64.clz, i64.popcount; r=sunfish MozReview-Commit-ID: 2XXloMWmbjV
js/src/asmjs/Wasm.cpp
js/src/asmjs/WasmIonCompile.cpp
js/src/jit-test/tests/wasm/basic-integer.js
js/src/jit-test/tests/wasm/spec/list.js
js/src/jit/IonTypes.h
js/src/jit/Lowering.cpp
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jit/shared/LIR-shared.h
js/src/jit/shared/LOpcodes-shared.h
js/src/jit/x64/CodeGenerator-x64.cpp
js/src/jit/x64/CodeGenerator-x64.h
mfbt/MathAlgorithms.h
--- a/js/src/asmjs/Wasm.cpp
+++ b/js/src/asmjs/Wasm.cpp
@@ -280,17 +280,17 @@ DecodeExpr(FunctionDecoder& f)
         return f.iter().readEnd(nullptr, nullptr, nullptr);
       case Expr::I32Clz:
       case Expr::I32Ctz:
       case Expr::I32Popcnt:
         return f.iter().readUnary(ValType::I32, nullptr);
       case Expr::I64Clz:
       case Expr::I64Ctz:
       case Expr::I64Popcnt:
-        return f.iter().notYetImplemented("i64") &&
+        return f.checkI64Support() &&
                f.iter().readUnary(ValType::I64, nullptr);
       case Expr::F32Abs:
       case Expr::F32Neg:
       case Expr::F32Ceil:
       case Expr::F32Floor:
       case Expr::F32Sqrt:
         return f.iter().readUnary(ValType::F32, nullptr);
       case Expr::F32Trunc:
--- a/js/src/asmjs/WasmIonCompile.cpp
+++ b/js/src/asmjs/WasmIonCompile.cpp
@@ -2908,16 +2908,22 @@ EmitExpr(FunctionCompiler& f)
         return EmitBitwise<MRsh>(f, ValType::I64, MIRType::Int64);
       case Expr::I64ShrU:
         return EmitBitwise<MUrsh>(f, ValType::I64, MIRType::Int64);
       case Expr::I64Rotr:
       case Expr::I64Rotl:
         return EmitRotate(f, ValType::I64, expr == Expr::I64Rotl);
       case Expr::I64Eqz:
         return EmitConversion<MNot>(f, ValType::I64, ValType::I32);
+      case Expr::I64Clz:
+        return EmitUnary<MClz>(f, ValType::I64);
+      case Expr::I64Ctz:
+        return EmitUnary<MCtz>(f, ValType::I64);
+      case Expr::I64Popcnt:
+        return EmitUnary<MPopcnt>(f, ValType::I64);
 
       // F32
       case Expr::F32Const: {
         float f32;
         if (!f.iter().readF32Const(&f32))
             return false;
 
         f.iter().setResult(f.constant(Float32Value(f32), MIRType::Float32));
@@ -3189,19 +3195,16 @@ EmitExpr(FunctionCompiler& f)
       case Expr::I64Load8U:
       case Expr::I64Load16U:
       case Expr::I64Load32U:
       case Expr::I64Load:
       case Expr::I64Store8:
       case Expr::I64Store16:
       case Expr::I64Store32:
       case Expr::I64Store:
-      case Expr::I64Clz:
-      case Expr::I64Ctz:
-      case Expr::I64Popcnt:
       case Expr::CurrentMemory:
       case Expr::GrowMemory:
         MOZ_CRASH("NYI");
       case Expr::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
@@ -208,19 +208,55 @@ if (hasI64()) {
     testComparison64('lt_u', "0x8000000012345678", "0x1", 0);
     testComparison64('le_s', -1, 0, 1);
     testComparison64('le_u', -1, -1, 1);
     testComparison64('gt_s', 1, "0x8000000000000000", 1);
     testComparison64('gt_u', 1, "0x8000000000000000", 0);
     testComparison64('ge_s', 1, "0x8000000000000000", 1);
     testComparison64('ge_u', 1, "0x8000000000000000", 0);
 
-    //testUnary('i64', 'clz', 40, 58); // TODO: NYI
-    //testUnary('i64', 'ctz', 40, 0); // TODO: NYI
-    //testUnary('i64', 'popcnt', 40, 0); // TODO: NYI
+    testUnary('i64', 'clz', 40, 58);
+    testUnary('i64', 'clz', "0x8000000000000000", 0);
+    testUnary('i64', 'clz', "0x7fffffffffffffff", 1);
+    testUnary('i64', 'clz', "0x4000000000000000", 1);
+    testUnary('i64', 'clz', "0x3000000000000000", 2);
+    testUnary('i64', 'clz', "0x2000000000000000", 2);
+    testUnary('i64', 'clz', "0x1000000000000000", 3);
+    testUnary('i64', 'clz', "0x0030000000000000", 10);
+    testUnary('i64', 'clz', "0x0000800000000000", 16);
+    testUnary('i64', 'clz', "0x00000000ffffffff", 32);
+    testUnary('i64', 'clz', -1, 0);
+    testUnary('i64', 'clz', 0, 64);
+
+    testUnary('i64', 'ctz', 40, 3);
+    testUnary('i64', 'ctz', "0x8000000000000000", 63);
+    testUnary('i64', 'ctz', "0x7fffffffffffffff", 0);
+    testUnary('i64', 'ctz', "0x4000000000000000", 62);
+    testUnary('i64', 'ctz', "0x3000000000000000", 60);
+    testUnary('i64', 'ctz', "0x2000000000000000", 61);
+    testUnary('i64', 'ctz', "0x1000000000000000", 60);
+    testUnary('i64', 'ctz', "0x0030000000000000", 52);
+    testUnary('i64', 'ctz', "0x0000800000000000", 47);
+    testUnary('i64', 'ctz', "0x00000000ffffffff", 0);
+    testUnary('i64', 'ctz', -1, 0);
+    testUnary('i64', 'ctz', 0, 64);
+
+    testUnary('i64', 'popcnt', 40, 2);
+    testUnary('i64', 'popcnt', 0, 0);
+    testUnary('i64', 'popcnt', "0x8000000000000000", 1);
+    testUnary('i64', 'popcnt', "0x7fffffffffffffff", 63);
+    testUnary('i64', 'popcnt', "0x4000000000000000", 1);
+    testUnary('i64', 'popcnt', "0x3000000000000000", 2);
+    testUnary('i64', 'popcnt', "0x2000000000000000", 1);
+    testUnary('i64', 'popcnt', "0x1000000000000000", 1);
+    testUnary('i64', 'popcnt', "0x0030000000000000", 2);
+    testUnary('i64', 'popcnt', "0x0000800000000000", 1);
+    testUnary('i64', 'popcnt', "0x00000000ffffffff", 32);
+    testUnary('i64', 'popcnt', -1, 64);
+    testUnary('i64', 'popcnt', 0, 0);
 
     testI64Eqz(40, 0);
     testI64Eqz(0, 1);
 
     setJitCompilerOption('wasm.test-mode', 0);
 } else {
     // Sleeper test: once i64 works on more platforms, remove this if-else.
     try {
--- a/js/src/jit-test/tests/wasm/spec/list.js
+++ b/js/src/jit-test/tests/wasm/spec/list.js
@@ -1,42 +1,42 @@
 // This is automatically generated by import_tests.sh. Do no modify by hand!
 var specTests = [];
 //specTests.push("address.wast");           // TODO wrapping offsets
 //specTests.push("binary.wast");            // TODO binary text format
 specTests.push("block_comments.wast");
 specTests.push("block.wast");
 specTests.push("break-drop.wast");
 //specTests.push("conversions.wast");       // TODO custom NaN
-//specTests.push("endianness.wast");        // TODO i64 ops
+//specTests.push("endianness.wast");        // TODO i64 loads/stores
 //specTests.push("exports.wast");           // TODO real memory exports
 //specTests.push("f32_cmp.wast");           // TODO custom NaN
 //specTests.push("f32.wast");               // TODO f32.trunc
 //specTests.push("f64_cmp.wast");           // TODO custom NaN
 //specTests.push("f64.wast");               // TODO f64.trunc
 specTests.push("fac.wast");
 //specTests.push("float_exprs.wast");       // TODO custom NaN
 //specTests.push("float_literals.wast");    // TODO custom NaN
 //specTests.push("float_memory.wast");      // TODO custom NaN
 //specTests.push("float_misc.wast");        // TODO copysign
 specTests.push("forward.wast");
 //specTests.push("func_ptrs.wast");         // TODO pass table index in bad indirect error message
 specTests.push("functions.wast");
 specTests.push("i32.wast");
-//specTests.push("i64.wast");               // TODO i64 ops
+specTests.push("i64.wast");
 specTests.push("imports.wast");
 specTests.push("int_exprs.wast");
 specTests.push("int_literals.wast");
 //specTests.push("labels.wast");            // TODO br_if value/cond
-//specTests.push("left-to-right.wast");     // TODO i64 ops
+//specTests.push("left-to-right.wast");     // TODO i64 loads/stores
 specTests.push("memory_redundancy.wast");
 //specTests.push("memory_trap.wast");       // TODO current_memory opcode
 //specTests.push("memory.wast");            // TODO alignment
 specTests.push("names.wast");
 //specTests.push("nan-propagation.wast");   // TODO f32 trunc
 //specTests.push("resizing.wast");          // TODO grow_memory opcode
 specTests.push("runaway-recursion.wast");
 //specTests.push("select.wast");            // TODO custom NaN
 //specTests.push("start.wast");             // TODO start opcode
-//specTests.push("store_retval.wast");      // TODO i64 ops
+//specTests.push("store_retval.wast");      // TODO i64 loads/stores
 //specTests.push("switch.wast");            // TODO value error!
 //specTests.push("traps.wast");             // TODO trap on OOB
 specTests.push("unreachable.wast");
--- a/js/src/jit/IonTypes.h
+++ b/js/src/jit/IonTypes.h
@@ -613,16 +613,23 @@ StringFromMIRType(MIRType type)
       return "Bool8x16";
     case MIRType::Doublex2:
       return "Doublex2";
   }
   MOZ_CRASH("Unknown MIRType.");
 }
 
 static inline bool
+IsIntType(MIRType type)
+{
+    return type == MIRType::Int32 ||
+           type == MIRType::Int64;
+}
+
+static inline bool
 IsNumberType(MIRType type)
 {
     return type == MIRType::Int32 ||
            type == MIRType::Double ||
            type == MIRType::Float32 ||
            type == MIRType::Int64;
 }
 
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -1401,36 +1401,60 @@ LIRGenerator::visitAbs(MAbs* ins)
     defineReuseInput(lir, ins, 0);
 }
 
 void
 LIRGenerator::visitClz(MClz* ins)
 {
     MDefinition* num = ins->num();
 
-    LClzI* lir = new(alloc()) LClzI(useRegisterAtStart(num));
-    define(lir, ins);
+    MOZ_ASSERT(IsIntType(ins->type()));
+
+    if (ins->type() == MIRType::Int32) {
+        LClzI* lir = new(alloc()) LClzI(useRegisterAtStart(num));
+        define(lir, ins);
+        return;
+    }
+
+    auto* lir = new(alloc()) LClzI64(useInt64RegisterAtStart(num));
+    defineInt64(lir, ins);
 }
 
 void
 LIRGenerator::visitCtz(MCtz* ins)
 {
     MDefinition* num = ins->num();
 
-    LCtzI* lir = new(alloc()) LCtzI(useRegisterAtStart(num));
-    define(lir, ins);
+    MOZ_ASSERT(IsIntType(ins->type()));
+
+    if (ins->type() == MIRType::Int32) {
+        LCtzI* lir = new(alloc()) LCtzI(useRegisterAtStart(num));
+        define(lir, ins);
+        return;
+    }
+
+    auto* lir = new(alloc()) LCtzI64(useInt64RegisterAtStart(num));
+    defineInt64(lir, ins);
 }
 
 void
 LIRGenerator::visitPopcnt(MPopcnt* ins)
 {
     MDefinition* num = ins->num();
 
-    LPopcntI* lir = new(alloc()) LPopcntI(useRegisterAtStart(num), temp());
-    define(lir, ins);
+    MOZ_ASSERT(IsIntType(ins->type()));
+
+    if (ins->type() == MIRType::Int32) {
+        LPopcntI* lir = new(alloc()) LPopcntI(useRegisterAtStart(num), temp());
+        define(lir, ins);
+        return;
+    }
+
+    auto* lir = new(alloc()) LPopcntI64(useInt64RegisterAtStart(num), tempInt64());
+    defineInt64(lir, ins);
 }
 
 void
 LIRGenerator::visitSqrt(MSqrt* ins)
 {
     MDefinition* num = ins->input();
     MOZ_ASSERT(IsFloatingPointType(num->type()));
 
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -5263,44 +5263,63 @@ MSqrt::trySpecializeFloat32(TempAllocato
     setResultType(MIRType::Float32);
     specialization_ = MIRType::Float32;
 }
 
 MDefinition*
 MClz::foldsTo(TempAllocator& alloc)
 {
     if (num()->isConstant()) {
-        int32_t n = num()->toConstant()->toInt32();
+        MConstant* c = num()->toConstant();
+        if (type() == MIRType::Int32) {
+            int32_t n = c->toInt32();
+            if (n == 0)
+                return MConstant::New(alloc, Int32Value(32));
+            return MConstant::New(alloc, Int32Value(mozilla::CountLeadingZeroes32(n)));
+        }
+        int64_t n = c->toInt64();
         if (n == 0)
-            return MConstant::New(alloc, Int32Value(32));
-        return MConstant::New(alloc, Int32Value(mozilla::CountLeadingZeroes32(n)));
+            return MConstant::NewInt64(alloc, int64_t(64));
+        return MConstant::NewInt64(alloc, int64_t(mozilla::CountLeadingZeroes64(n)));
     }
 
     return this;
 }
 
 MDefinition*
 MCtz::foldsTo(TempAllocator& alloc)
 {
     if (num()->isConstant()) {
-        int32_t n = num()->toConstant()->toInt32();
+        MConstant* c = num()->toConstant();
+        if (type() == MIRType::Int32) {
+            int32_t n = num()->toConstant()->toInt32();
+            if (n == 0)
+                return MConstant::New(alloc, Int32Value(32));
+            return MConstant::New(alloc, Int32Value(mozilla::CountTrailingZeroes32(n)));
+        }
+        int64_t n = c->toInt64();
         if (n == 0)
-            return MConstant::New(alloc, Int32Value(32));
-        return MConstant::New(alloc, Int32Value(mozilla::CountTrailingZeroes32(n)));
+            return MConstant::NewInt64(alloc, int64_t(64));
+        return MConstant::NewInt64(alloc, int64_t(mozilla::CountTrailingZeroes64(n)));
     }
 
     return this;
 }
 
 MDefinition*
 MPopcnt::foldsTo(TempAllocator& alloc)
 {
     if (num()->isConstant()) {
-        int32_t n = num()->toConstant()->toInt32();
-        return MConstant::New(alloc, Int32Value(mozilla::CountPopulation32(n)));
+        MConstant* c = num()->toConstant();
+        if (type() == MIRType::Int32) {
+            int32_t n = num()->toConstant()->toInt32();
+            return MConstant::New(alloc, Int32Value(mozilla::CountPopulation32(n)));
+        }
+        int64_t n = c->toInt64();
+        return MConstant::NewInt64(alloc, int64_t(mozilla::CountPopulation64(n)));
     }
 
     return this;
 }
 
 MDefinition*
 MBoundsCheck::foldsTo(TempAllocator& alloc)
 {
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -6207,33 +6207,34 @@ class MAbs
 };
 
 class MClz
   : public MUnaryInstruction
   , public BitwisePolicy::Data
 {
     bool operandIsNeverZero_;
 
-    explicit MClz(MDefinition* num)
+    explicit MClz(MDefinition* num, MIRType type)
       : MUnaryInstruction(num),
         operandIsNeverZero_(false)
     {
+        MOZ_ASSERT(IsIntType(type));
         MOZ_ASSERT(IsNumberType(num->type()));
-        specialization_ = MIRType::Int32;
-        setResultType(MIRType::Int32);
+        specialization_ = type;
+        setResultType(type);
         setMovable();
     }
 
   public:
     INSTRUCTION_HEADER(Clz)
     static MClz* New(TempAllocator& alloc, MDefinition* num) {
-        return new(alloc) MClz(num);
+        return new(alloc) MClz(num, MIRType::Int32);
     }
     static MClz* NewAsmJS(TempAllocator& alloc, MDefinition* num) {
-        return new(alloc) MClz(num);
+        return new(alloc) MClz(num, num->type());
     }
     MDefinition* num() const {
         return getOperand(0);
     }
     bool congruentTo(const MDefinition* ins) const override {
         return congruentIfOperandsEqual(ins);
     }
 
@@ -6251,33 +6252,34 @@ class MClz
 };
 
 class MCtz
   : public MUnaryInstruction
   , public BitwisePolicy::Data
 {
     bool operandIsNeverZero_;
 
-    explicit MCtz(MDefinition* num)
+    explicit MCtz(MDefinition* num, MIRType type)
       : MUnaryInstruction(num),
         operandIsNeverZero_(false)
     {
+        MOZ_ASSERT(IsIntType(type));
         MOZ_ASSERT(IsNumberType(num->type()));
-        specialization_ = MIRType::Int32;
-        setResultType(MIRType::Int32);
+        specialization_ = type;
+        setResultType(type);
         setMovable();
     }
 
   public:
     INSTRUCTION_HEADER(Ctz)
     static MCtz* New(TempAllocator& alloc, MDefinition* num) {
-        return new(alloc) MCtz(num);
+        return new(alloc) MCtz(num, MIRType::Int32);
     }
     static MCtz* NewAsmJS(TempAllocator& alloc, MDefinition* num) {
-        return new(alloc) MCtz(num);
+        return new(alloc) MCtz(num, num->type());
     }
     MDefinition* num() const {
         return getOperand(0);
     }
     bool congruentTo(const MDefinition* ins) const override {
         return congruentIfOperandsEqual(ins);
     }
 
@@ -6293,32 +6295,33 @@ class MCtz
     void computeRange(TempAllocator& alloc) override;
     void collectRangeInfoPreTrunc() override;
 };
 
 class MPopcnt
   : public MUnaryInstruction
   , public BitwisePolicy::Data
 {
-    explicit MPopcnt(MDefinition* num)
+    explicit MPopcnt(MDefinition* num, MIRType type)
       : MUnaryInstruction(num)
     {
         MOZ_ASSERT(IsNumberType(num->type()));
-        specialization_ = MIRType::Int32;
-        setResultType(MIRType::Int32);
+        MOZ_ASSERT(IsIntType(type));
+        specialization_ = type;
+        setResultType(type);
         setMovable();
     }
 
   public:
     INSTRUCTION_HEADER(Popcnt)
     static MPopcnt* New(TempAllocator& alloc, MDefinition* num) {
-        return new(alloc) MPopcnt(num);
+        return new(alloc) MPopcnt(num, MIRType::Int32);
     }
     static MPopcnt* NewAsmJS(TempAllocator& alloc, MDefinition* num) {
-        return new(alloc) MPopcnt(num);
+        return new(alloc) MPopcnt(num, num->type());
     }
     MDefinition* num() const {
         return getOperand(0);
     }
     bool congruentTo(const MDefinition* ins) const override {
         return congruentIfOperandsEqual(ins);
     }
 
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -3259,45 +3259,73 @@ class LAbsF : public LInstructionHelper<
 {
   public:
     LIR_HEADER(AbsF)
     explicit LAbsF(const LAllocation& num) {
         setOperand(0, num);
     }
 };
 
-// Count leading zeroes
+// Count leading zeroes on an int32.
 class LClzI : public LInstructionHelper<1, 1, 0>
 {
   public:
     LIR_HEADER(ClzI)
     explicit LClzI(const LAllocation& num) {
         setOperand(0, num);
     }
 
     MClz* mir() const {
         return mir_->toClz();
     }
 };
 
-// Count trailing zeroes
+// Count leading zeroes on an int64.
+class LClzI64 : public LInstructionHelper<INT64_PIECES, INT64_PIECES, 0>
+{
+  public:
+    LIR_HEADER(ClzI64)
+    explicit LClzI64(const LInt64Allocation& num) {
+        setInt64Operand(0, num);
+    }
+
+    MClz* mir() const {
+        return mir_->toClz();
+    }
+};
+
+// Count trailing zeroes on an int32.
 class LCtzI : public LInstructionHelper<1, 1, 0>
 {
   public:
     LIR_HEADER(CtzI)
     explicit LCtzI(const LAllocation& num) {
         setOperand(0, num);
     }
 
     MCtz* mir() const {
         return mir_->toCtz();
     }
 };
 
-// Count population
+// Count trailing zeroes on an int64.
+class LCtzI64 : public LInstructionHelper<INT64_PIECES, INT64_PIECES, 0>
+{
+  public:
+    LIR_HEADER(CtzI64)
+    explicit LCtzI64(const LInt64Allocation& num) {
+        setInt64Operand(0, num);
+    }
+
+    MCtz* mir() const {
+        return mir_->toCtz();
+    }
+};
+
+// Count population on an int32.
 class LPopcntI : public LInstructionHelper<1, 1, 1>
 {
   public:
     LIR_HEADER(PopcntI)
     explicit LPopcntI(const LAllocation& num, const LDefinition& temp) {
         setOperand(0, num);
         setTemp(0, temp);
     }
@@ -3306,16 +3334,31 @@ class LPopcntI : public LInstructionHelp
         return mir_->toPopcnt();
     }
 
     const LDefinition* temp() {
         return getTemp(0);
     }
 };
 
+// Count population on an int64.
+class LPopcntI64 : public LInstructionHelper<INT64_PIECES, INT64_PIECES, 1>
+{
+  public:
+    LIR_HEADER(PopcntI64)
+    explicit LPopcntI64(const LInt64Allocation& num, const LInt64Definition& temp) {
+        setInt64Operand(0, num);
+        setInt64Temp(0, temp);
+    }
+
+    MPopcnt* mir() const {
+        return mir_->toPopcnt();
+    }
+};
+
 // Square root of a double.
 class LSqrtD : public LInstructionHelper<1, 1, 0>
 {
   public:
     LIR_HEADER(SqrtD)
     explicit LSqrtD(const LAllocation& num) {
         setOperand(0, num);
     }
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -137,18 +137,21 @@
     _(MinMaxF)                      \
     _(NegI)                         \
     _(NegD)                         \
     _(NegF)                         \
     _(AbsI)                         \
     _(AbsD)                         \
     _(AbsF)                         \
     _(ClzI)                         \
+    _(ClzI64)                       \
     _(CtzI)                         \
+    _(CtzI64)                       \
     _(PopcntI)                      \
+    _(PopcntI64)                    \
     _(SqrtD)                        \
     _(SqrtF)                        \
     _(Atan2D)                       \
     _(Hypot)                        \
     _(PowI)                         \
     _(PowD)                         \
     _(PowHalfD)                     \
     _(Random)                       \
--- a/js/src/jit/x64/CodeGenerator-x64.cpp
+++ b/js/src/jit/x64/CodeGenerator-x64.cpp
@@ -1311,8 +1311,88 @@ CodeGeneratorX64::visitInt64ToFloatingPo
 }
 
 void
 CodeGeneratorX64::visitNotI64(LNotI64* lir)
 {
     masm.cmpq(Imm32(0), ToRegister(lir->input()));
     masm.emitSet(Assembler::Equal, ToRegister(lir->output()));
 }
+
+void
+CodeGeneratorX64::visitClzI64(LClzI64* lir)
+{
+    Register input = ToRegister(lir->input());
+    Register output = ToRegister(lir->output());
+
+    Label nonzero;
+    masm.bsrq(input, output);
+
+    masm.j(Assembler::NonZero, &nonzero);
+    // 0x7f ^ 0x3f == 0x40 == 64
+    masm.movq(ImmWord(0x7f), output);
+
+    masm.bind(&nonzero);
+    masm.xorq(Imm32(0x3f), output);
+}
+
+void
+CodeGeneratorX64::visitCtzI64(LCtzI64* lir)
+{
+    Register input = ToRegister(lir->input());
+    Register output = ToRegister(lir->output());
+
+    Label nonzero;
+    masm.bsfq(input, output);
+
+    masm.j(Assembler::NonZero, &nonzero);
+    masm.movq(ImmWord(64), output);
+
+    masm.bind(&nonzero);
+}
+
+void
+CodeGeneratorX64::visitPopcntI64(LPopcntI64* lir)
+{
+    Register input = ToRegister(lir->input());
+    Register output = ToRegister(lir->output());
+
+    if (AssemblerX86Shared::HasPOPCNT()) {
+        masm.popcntq(input, output);
+        return;
+    }
+
+    Register tmp = ToRegister(lir->getTemp(0));
+    if (input != output)
+        masm.movq(input, output);
+
+    MOZ_ASSERT(tmp != output);
+
+    ScratchRegisterScope scratch(masm);
+
+    // Equivalent to mozilla::CountPopulation32, adapted for 64 bits.
+    // x -= (x >> 1) & m1;
+    masm.movq(input, tmp);
+    masm.movq(ImmWord(0x5555555555555555), scratch);
+    masm.shrq(Imm32(1), tmp);
+    masm.andq(scratch, tmp);
+    masm.subq(tmp, output);
+
+    // x = (x & m2) + ((x >> 2) & m2);
+    masm.movq(output, tmp);
+    masm.movq(ImmWord(0x3333333333333333), scratch);
+    masm.andq(scratch, output);
+    masm.shrq(Imm32(2), tmp);
+    masm.andq(scratch, tmp);
+    masm.addq(tmp, output);
+
+    // x = (x + (x >> 4)) & m4;
+    masm.movq(output, tmp);
+    masm.movq(ImmWord(0x0f0f0f0f0f0f0f0f), scratch);
+    masm.shrq(Imm32(4), tmp);
+    masm.addq(tmp, output);
+    masm.andq(scratch, output);
+
+    // (x * h01) >> 56
+    masm.movq(ImmWord(0x0101010101010101), scratch);
+    masm.imulq(scratch, output);
+    masm.shrq(Imm32(56), output);
+}
--- a/js/src/jit/x64/CodeGenerator-x64.h
+++ b/js/src/jit/x64/CodeGenerator-x64.h
@@ -48,16 +48,19 @@ class CodeGeneratorX64 : public CodeGene
     void visitShiftI64(LShiftI64* lir);
     void visitRotate64(LRotate64* lir);
     void visitAddI64(LAddI64* lir);
     void visitSubI64(LSubI64* lir);
     void visitMulI64(LMulI64* lir);
     void visitDivOrModI64(LDivOrModI64* lir);
     void visitUDivOrMod64(LUDivOrMod64* lir);
     void visitNotI64(LNotI64* lir);
+    void visitClzI64(LClzI64* lir);
+    void visitCtzI64(LCtzI64* lir);
+    void visitPopcntI64(LPopcntI64* lir);
     void visitTruncateDToInt32(LTruncateDToInt32* ins);
     void visitTruncateFToInt32(LTruncateFToInt32* ins);
     void visitWrapInt64ToInt32(LWrapInt64ToInt32* lir);
     void visitExtendInt32ToInt64(LExtendInt32ToInt64* lir);
     void visitWasmTruncateToInt64(LWasmTruncateToInt64* lir);
     void visitInt64ToFloatingPoint(LInt64ToFloatingPoint* lir);
     void visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins);
     void visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins);
--- a/mfbt/MathAlgorithms.h
+++ b/mfbt/MathAlgorithms.h
@@ -335,17 +335,17 @@ CountTrailingZeroes32(uint32_t aValue)
  * Compute the number of one bits in the number |aValue|,
  */
 inline uint_fast8_t
 CountPopulation32(uint32_t aValue)
 {
   return detail::CountPopulation32(aValue);
 }
 
-/** Analogous to CoutPopulation32, but for 64-bit numbers */
+/** Analogous to CountPopulation32, but for 64-bit numbers */
 inline uint_fast8_t
 CountPopulation64(uint64_t aValue)
 {
   return detail::CountPopulation64(aValue);
 }
 
 /** Analogous to CountLeadingZeroes32, but for 64-bit numbers. */
 inline uint_fast8_t