Commit be579d4797

Luuk de Gram <luuk@degram.dev>
2022-03-21 22:03:25
wasm: Implement @popCount
1 parent 7141356
Changed files (4)
src
test
behavior
src/arch/wasm/CodeGen.zig
@@ -1371,6 +1371,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
         .aggregate_init => self.airAggregateInit(inst),
         .union_init => self.airUnionInit(inst),
         .prefetch => self.airPrefetch(inst),
+        .popcount => self.airPopcount(inst),
 
         .slice => self.airSlice(inst),
         .slice_len => self.airSliceLen(inst),
@@ -1419,7 +1420,6 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
         .frame_addr,
         .clz,
         .ctz,
-        .popcount,
         .byte_swap,
         .bit_reverse,
         .is_err_ptr,
@@ -3565,3 +3565,49 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
     try self.memcpy(dst, src, len);
     return WValue{ .none = {} };
 }
+
+fn airPopcount(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
+    if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
+    const ty_op = self.air.instructions.items(.data)[inst].ty_op;
+    const operand = try self.resolveInst(ty_op.operand);
+    const op_ty = self.air.typeOf(ty_op.operand);
+
+    if (op_ty.zigTypeTag() == .Vector) {
+        return self.fail("TODO: Implement @popCount for vectors", .{});
+    }
+
+    const int_info = op_ty.intInfo(self.target);
+    const bits = int_info.bits;
+    const wasm_bits = toWasmBits(bits) orelse {
+        return self.fail("TODO: Implement @popCount for integers with bitsize '{d}'", .{bits});
+    };
+
+    try self.emitWValue(operand);
+
+    // for signed integers we first mask the signedness bit
+    if (int_info.signedness == .signed and wasm_bits != bits) {
+        switch (wasm_bits) {
+            32 => {
+                const mask = (@as(u32, 1) << @intCast(u5, bits)) - 1;
+                try self.addImm32(@bitCast(i32, mask));
+                try self.addTag(.i32_and);
+            },
+            64 => {
+                const mask = (@as(u64, 1) << @intCast(u6, bits)) - 1;
+                try self.addImm64(mask);
+                try self.addTag(.i64_and);
+            },
+            else => unreachable,
+        }
+    }
+
+    switch (wasm_bits) {
+        32 => try self.addTag(.i32_popcnt),
+        64 => try self.addTag(.i64_popcnt),
+        else => unreachable,
+    }
+
+    const result = try self.allocLocal(op_ty);
+    try self.addLabel(.local_set, result.local);
+    return result;
+}
src/arch/wasm/Emit.zig
@@ -207,6 +207,8 @@ pub fn emitMir(emit: *Emit) InnerError!void {
             .i32_rem_u => try emit.emitTag(tag),
             .i64_rem_s => try emit.emitTag(tag),
             .i64_rem_u => try emit.emitTag(tag),
+            .i32_popcnt => try emit.emitTag(tag),
+            .i64_popcnt => try emit.emitTag(tag),
 
             .extended => try emit.emitExtended(inst),
         }
src/arch/wasm/Mir.zig
@@ -317,6 +317,8 @@ pub const Inst = struct {
         /// Uses `tag`
         f64_ge = 0x66,
         /// Uses `tag`
+        i32_popcnt = 0x69,
+        /// Uses `tag`
         i32_add = 0x6A,
         /// Uses `tag`
         i32_sub = 0x6B,
@@ -343,6 +345,8 @@ pub const Inst = struct {
         /// Uses `tag`
         i32_shr_u = 0x76,
         /// Uses `tag`
+        i64_popcnt = 0x7B,
+        /// Uses `tag`
         i64_add = 0x7C,
         /// Uses `tag`
         i64_sub = 0x7D,
test/behavior/popcount.zig
@@ -4,7 +4,6 @@ const expect = std.testing.expect;
 const expectEqual = std.testing.expectEqual;
 
 test "@popCount integers" {
-    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
@@ -13,6 +12,25 @@ test "@popCount integers" {
     try testPopCountIntegers();
 }
 
+test "@popCount 128bit integer" {
+    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+
+    comptime {
+        try expect(@popCount(u128, @as(u128, 0b11111111000110001100010000100001000011000011100101010001)) == 24);
+        try expect(@popCount(i128, @as(i128, 0b11111111000110001100010000100001000011000011100101010001)) == 24);
+    }
+
+    {
+        var x: u128 = 0b11111111000110001100010000100001000011000011100101010001;
+        try expect(@popCount(u128, x) == 24);
+    }
+
+    try expect(@popCount(i128, @as(i128, 0b11111111000110001100010000100001000011000011100101010001)) == 24);
+}
+
 fn testPopCountIntegers() !void {
     {
         var x: u32 = 0xffffffff;
@@ -42,16 +60,9 @@ fn testPopCountIntegers() !void {
         var x: i8 = -120;
         try expect(@popCount(i8, x) == 2);
     }
-    {
-        var x: u128 = 0b11111111000110001100010000100001000011000011100101010001;
-        try expect(@popCount(u128, x) == 24);
-    }
     comptime {
         try expect(@popCount(u8, @bitCast(u8, @as(i8, -120))) == 2);
     }
-    comptime {
-        try expect(@popCount(i128, @as(i128, 0b11111111000110001100010000100001000011000011100101010001)) == 24);
-    }
 }
 
 test "@popCount vectors" {