authorgravatar for auguste.rame@gmail.comAuguste Rame <auguste.rame@gmail.com> 2021-07-25 20:35:55-04:00
committergravatar for auguste.rame@gmail.comAuguste Rame <auguste.rame@gmail.com> 2021-07-25 20:35:55-04:00
logecca829bcbfdcb03bfcefa56f530b180d5c61653
tree3a948e1e290a8a96f9d90e8119a1bd5f7001483f
parent653c851e6233a105cb151602517d0222b1128ea7

Add vector support for @popCount


6 files changed, 96 insertions(+), 15 deletions(-)

doc/langref.html.in+5-3
...@@ -8105,12 +8105,14 @@ test "@wasmMemoryGrow" {...@@ -8105,12 +8105,14 @@ test "@wasmMemoryGrow" {
8105 {#header_close#}8105 {#header_close#}
81068106
8107 {#header_open|@popCount#}8107 {#header_open|@popCount#}
8108 <pre>{#syntax#}@popCount(comptime T: type, integer: T){#endsyntax#}</pre>8108 <pre>{#syntax#}@popCount(comptime T: type, operand: T){#endsyntax#}</pre>
8109 <p>{#syntax#}T{#endsyntax#} must be an integer type.</p>
8110 <p>{#syntax#}operand{#endsyntax#} may be an {#link|integer|Integers#} or {#link|vector|Vectors#}.</p>
8109 <p>Counts the number of bits set in an integer.</p>8111 <p>Counts the number of bits set in an integer.</p>
8110 <p>8112 <p>
8111 If {#syntax#}integer{#endsyntax#} is known at {#link|comptime#},8113 If {#syntax#}operand{#endsyntax#} is a {#link|comptime#}-known integer,
8112 the return type is {#syntax#}comptime_int{#endsyntax#}.8114 the return type is {#syntax#}comptime_int{#endsyntax#}.
8113 Otherwise, the return type is an unsigned integer with the minimum number8115 Otherwise, the return type is an unsigned integer or vector of unsigned integers with the minimum number
8114 of bits that can represent the bit count of the integer type.8116 of bits that can represent the bit count of the integer type.
8115 </p>8117 </p>
8116 {#see_also|@ctz|@clz#}8118 {#see_also|@ctz|@clz#}
src/stage1/all_types.hpp+1
...@@ -1913,6 +1913,7 @@ struct ZigLLVMFnKey {...@@ -1913,6 +1913,7 @@ struct ZigLLVMFnKey {
1913 } clz;1913 } clz;
1914 struct {1914 struct {
1915 uint32_t bit_count;1915 uint32_t bit_count;
1916 uint32_t vector_len; // 0 means not a vector
1916 } pop_count;1917 } pop_count;
1917 struct {1918 struct {
1918 BuiltinFnId op;1919 BuiltinFnId op;
src/stage1/analyze.cpp+2-1
...@@ -7887,7 +7887,8 @@ uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey const *x) {...@@ -7887,7 +7887,8 @@ uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey const *x) {
7887 case ZigLLVMFnIdClz:7887 case ZigLLVMFnIdClz:
7888 return (uint32_t)(x->data.clz.bit_count) * (uint32_t)2428952817;7888 return (uint32_t)(x->data.clz.bit_count) * (uint32_t)2428952817;
7889 case ZigLLVMFnIdPopCount:7889 case ZigLLVMFnIdPopCount:
7890 return (uint32_t)(x->data.clz.bit_count) * (uint32_t)101195049;7890 return (uint32_t)(x->data.pop_count.bit_count) * (uint32_t)101195049 +
7891 (uint32_t)(x->data.pop_count.vector_len) * (((uint32_t)x->id << 5) + 1025);
7891 case ZigLLVMFnIdFloatOp:7892 case ZigLLVMFnIdFloatOp:
7892 return (uint32_t)(x->data.floating.bit_count) * ((uint32_t)x->id + 1025) +7893 return (uint32_t)(x->data.floating.bit_count) * ((uint32_t)x->id + 1025) +
7893 (uint32_t)(x->data.floating.vector_len) * (((uint32_t)x->id << 5) + 1025) +7894 (uint32_t)(x->data.floating.vector_len) * (((uint32_t)x->id << 5) + 1025) +
src/stage1/codegen.cpp+1
...@@ -5053,6 +5053,7 @@ static LLVMValueRef get_int_builtin_fn(CodeGen *g, ZigType *expr_type, BuiltinFn...@@ -5053,6 +5053,7 @@ static LLVMValueRef get_int_builtin_fn(CodeGen *g, ZigType *expr_type, BuiltinFn
5053 n_args = 1;5053 n_args = 1;
5054 key.id = ZigLLVMFnIdPopCount;5054 key.id = ZigLLVMFnIdPopCount;
5055 key.data.pop_count.bit_count = (uint32_t)int_type->data.integral.bit_count;5055 key.data.pop_count.bit_count = (uint32_t)int_type->data.integral.bit_count;
5056 key.data.pop_count.vector_len = vector_len;
5056 } else if (fn_id == BuiltinFnIdBswap) {5057 } else if (fn_id == BuiltinFnIdBswap) {
5057 fn_name = "bswap";5058 fn_name = "bswap";
5058 n_args = 1;5059 n_args = 1;
src/stage1/ir.cpp+60-6
...@@ -15997,33 +15997,87 @@ static Stage1AirInst *ir_analyze_instruction_clz(IrAnalyze *ira, Stage1ZirInstCl...@@ -15997,33 +15997,87 @@ static Stage1AirInst *ir_analyze_instruction_clz(IrAnalyze *ira, Stage1ZirInstCl
15997}15997}
1599815998
15999static Stage1AirInst *ir_analyze_instruction_pop_count(IrAnalyze *ira, Stage1ZirInstPopCount *instruction) {15999static Stage1AirInst *ir_analyze_instruction_pop_count(IrAnalyze *ira, Stage1ZirInstPopCount *instruction) {
16000 Error err;
16001
16000 ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child);16002 ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child);
16001 if (type_is_invalid(int_type))16003 if (type_is_invalid(int_type))
16002 return ira->codegen->invalid_inst_gen;16004 return ira->codegen->invalid_inst_gen;
1600316005
16004 Stage1AirInst *op = ir_implicit_cast(ira, instruction->op->child, int_type);16006 Stage1AirInst *uncasted_op = instruction->op->child;
16007 if (type_is_invalid(uncasted_op->value->type))
16008 return ira->codegen->invalid_inst_gen;
16009
16010 uint32_t vector_len = UINT32_MAX; // means not a vector
16011 if (uncasted_op->value->type->id == ZigTypeIdArray) {
16012 bool can_be_vec_elem;
16013 if ((err = is_valid_vector_elem_type(ira->codegen, uncasted_op->value->type->data.array.child_type,
16014 &can_be_vec_elem)))
16015 {
16016 return ira->codegen->invalid_inst_gen;
16017 }
16018 if (can_be_vec_elem) {
16019 vector_len = uncasted_op->value->type->data.array.len;
16020 }
16021 } else if (uncasted_op->value->type->id == ZigTypeIdVector) {
16022 vector_len = uncasted_op->value->type->data.vector.len;
16023 }
16024
16025 bool is_vector = (vector_len != UINT32_MAX);
16026 ZigType *op_type = is_vector ? get_vector_type(ira->codegen, vector_len, int_type) : int_type;
16027
16028 Stage1AirInst *op = ir_implicit_cast(ira, uncasted_op, op_type);
16005 if (type_is_invalid(op->value->type))16029 if (type_is_invalid(op->value->type))
16006 return ira->codegen->invalid_inst_gen;16030 return ira->codegen->invalid_inst_gen;
1600716031
16008 if (int_type->data.integral.bit_count == 0)16032 if (int_type->data.integral.bit_count == 0)
16009 return ir_const_unsigned(ira, instruction->base.scope, instruction->base.source_node, 0);16033 return ir_const_unsigned(ira, instruction->base.scope, instruction->base.source_node, 0);
1601016034
16035 ZigType *smallest_type = get_smallest_unsigned_int_type(ira->codegen, int_type->data.integral.bit_count);
16036
16011 if (instr_is_comptime(op)) {16037 if (instr_is_comptime(op)) {
16012 ZigValue *val = ir_resolve_const(ira, op, UndefOk);16038 ZigValue *val = ir_resolve_const(ira, op, UndefOk);
16013 if (val == nullptr)16039 if (val == nullptr)
16014 return ira->codegen->invalid_inst_gen;16040 return ira->codegen->invalid_inst_gen;
16015 if (val->special == ConstValSpecialUndef)16041 if (val->special == ConstValSpecialUndef)
16016 return ir_const_undef(ira, instruction->base.scope, instruction->base.source_node, ira->codegen->builtin_types.entry_num_lit_int);16042 return ir_const_undef(ira, instruction->base.scope, instruction->base.source_node, ira->codegen->builtin_types.entry_num_lit_int);
16043
16044 if (is_vector) {
16045 ZigType *smallest_vec_type = get_vector_type(ira->codegen, vector_len, smallest_type);
16046 Stage1AirInst *result = ir_const(ira, instruction->base.scope, instruction->base.source_node, smallest_vec_type);
16047 expand_undef_array(ira->codegen, val);
16048 result->value->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate<ZigValue>(smallest_vec_type->data.vector.len);
16049 for (unsigned i = 0; i < smallest_vec_type->data.vector.len; i += 1) {
16050 ZigValue *op_elem_val = &val->data.x_array.data.s_none.elements[i];
16051 if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, instruction->base.source_node,
16052 op_elem_val, UndefOk)))
16053 {
16054 return ira->codegen->invalid_inst_gen;
16055 }
16056 ZigValue *result_elem_val = &result->value->data.x_array.data.s_none.elements[i];
16057 result_elem_val->type = smallest_type;
16058 result_elem_val->special = op_elem_val->special;
16059 if (op_elem_val->special == ConstValSpecialUndef)
16060 continue;
1601716061
16018 if (bigint_cmp_zero(&val->data.x_bigint) != CmpLT) {16062 if (bigint_cmp_zero(&op_elem_val->data.x_bigint) != CmpLT) {
16019 size_t result = bigint_popcount_unsigned(&val->data.x_bigint);16063 size_t value = bigint_popcount_unsigned(&op_elem_val->data.x_bigint);
16064 bigint_init_unsigned(&result->value->data.x_array.data.s_none.elements[i].data.x_bigint, value);
16065 }
16066 size_t value = bigint_popcount_signed(&op_elem_val->data.x_bigint, int_type->data.integral.bit_count);
16067 bigint_init_unsigned(&result->value->data.x_array.data.s_none.elements[i].data.x_bigint, value);
16068 }
16069 return result;
16070 } else {
16071 if (bigint_cmp_zero(&val->data.x_bigint) != CmpLT) {
16072 size_t result = bigint_popcount_unsigned(&val->data.x_bigint);
16073 return ir_const_unsigned(ira, instruction->base.scope, instruction->base.source_node, result);
16074 }
16075 size_t result = bigint_popcount_signed(&val->data.x_bigint, int_type->data.integral.bit_count);
16020 return ir_const_unsigned(ira, instruction->base.scope, instruction->base.source_node, result);16076 return ir_const_unsigned(ira, instruction->base.scope, instruction->base.source_node, result);
16021 }16077 }
16022 size_t result = bigint_popcount_signed(&val->data.x_bigint, int_type->data.integral.bit_count);
16023 return ir_const_unsigned(ira, instruction->base.scope, instruction->base.source_node, result);
16024 }16078 }
1602516079
16026 ZigType *return_type = get_smallest_unsigned_int_type(ira->codegen, int_type->data.integral.bit_count);16080 ZigType *return_type = is_vector ? get_vector_type(ira->codegen, vector_len, smallest_type) : smallest_type;
16027 return ir_build_pop_count_gen(ira, instruction->base.scope, instruction->base.source_node, return_type, op);16081 return ir_build_pop_count_gen(ira, instruction->base.scope, instruction->base.source_node, return_type, op);
16028}16082}
1602916083
test/behavior/popcount.zig+27-5
...@@ -1,11 +1,14 @@...@@ -1,11 +1,14 @@
1const expect = @import("std").testing.expect;1const std = @import("std");
2const expect = std.testing.expect;
3const expectEqual = std.testing.expectEqual;
4const Vector = std.meta.Vector;
25
3test "@popCount" {6test "@popCount integers" {
4 comptime try testPopCount();7 comptime try testPopCountIntegers();
5 try testPopCount();8 try testPopCountIntegers();
6}9}
710
8fn testPopCount() !void {11fn testPopCountIntegers() !void {
9 {12 {
10 var x: u32 = 0xffffffff;13 var x: u32 = 0xffffffff;
11 try expect(@popCount(u32, x) == 32);14 try expect(@popCount(u32, x) == 32);
...@@ -41,3 +44,22 @@ fn testPopCount() !void {...@@ -41,3 +44,22 @@ fn testPopCount() !void {
41 try expect(@popCount(i128, 0b11111111000110001100010000100001000011000011100101010001) == 24);44 try expect(@popCount(i128, 0b11111111000110001100010000100001000011000011100101010001) == 24);
42 }45 }
43}46}
47
48test "@popCount vectors" {
49 // https://github.com/ziglang/zig/issues/3317
50 if (std.Target.current.cpu.arch == .mipsel or std.Target.current.cpu.arch == .mips) return error.SkipZigTest;
51
52 comptime try testPopCountVectors();
53 try testPopCountVectors();
54}
55
56fn testPopCountVectors() !void {
57 {
58 var x: Vector(8, u32) = [1]u32{0xffffffff} ** 8;
59 try expectEqual([1]u6{32} ** 8, @as([8]u6, @popCount(u32, x)));
60 }
61 {
62 var x: Vector(8, i16) = [1]i16{-1} ** 8;
63 try expectEqual([1]u5{16} ** 8, @as([8]u5, @popCount(i16, x)));
64 }
65}