From be579d479753153e78e55b17cb8fd2d55004145b Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Mon, 21 Mar 2022 22:03:25 +0100 Subject: [PATCH] wasm: Implement @popCount --- src/arch/wasm/CodeGen.zig | 48 +++++++++++++++++++++++++++++++++++++- src/arch/wasm/Emit.zig | 2 ++ src/arch/wasm/Mir.zig | 4 ++++ test/behavior/popcount.zig | 27 ++++++++++++++------- 4 files changed, 72 insertions(+), 9 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 8d515e9365..7d55e95f23 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/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; +} diff --git a/src/arch/wasm/Emit.zig b/src/arch/wasm/Emit.zig index e0b40794b8..f0f4cfae5d 100644 --- a/src/arch/wasm/Emit.zig +++ b/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), } diff --git a/src/arch/wasm/Mir.zig b/src/arch/wasm/Mir.zig index b24af64ff5..09a0c32901 100644 --- a/src/arch/wasm/Mir.zig +++ b/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, diff --git a/test/behavior/popcount.zig b/test/behavior/popcount.zig index 16f33f5514..9658e694a8 100644 --- a/test/behavior/popcount.zig +++ b/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" {