Support bit_set[E; [4]u64] style types

This commit is contained in:
gingerBill committed 2026-09-29 12:17:02 +01:00
1 parent ae63a4a4c6
commit b153d6071c
6 files changed
+236 -21

No files matched your search

+12 -1
View File
@@ -1652,7 +1652,18 @@ when MAP_ENABLED {
// `card` returns the number of bits that are set in a bit_set—its cardinality
@builtin
card :: proc "contextless" (s: $S/bit_set[$E; $U]) -> int {
return int(intrinsics.count_ones(transmute(intrinsics.type_bit_set_underlying_type(S))s))
Backing :: intrinsics.type_bit_set_underlying_type(S)
when intrinsics.type_is_array(Backing) {
// bit_set backed by an array of integers: sum the population count of each element
backing := transmute(Backing)s
count := 0
for elem in backing {
count += int(intrinsics.count_ones(elem))
}
return count
} else {
return int(intrinsics.count_ones(transmute(Backing)s))
}
}
+60
View File
@@ -1815,6 +1815,14 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "", verb: rune = 'v') {
fmt_bit_set(fi, val, info.name, verb)
case runtime.Type_Info_Bit_Set:
if info.underlying != nil {
#partial switch _ in runtime.type_info_base(info.underlying).variant {
case runtime.Type_Info_Array:
fmt_bit_set_array(fi, v, type_info, name, verb)
return
}
}
bits: u128
bit_size := u128(8*type_info.size)
@@ -1914,6 +1922,58 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "", verb: rune = 'v') {
}
}
// Formats an array-of-integers backed bit_set (e.g. `bit_set[E; [4]u64]`).
// The bits are stored as a little-endian sequence in memory (bit `b` is bit `b%8` of byte `b/8`),
// so they can be scanned directly regardless of how many array elements back the set.
fmt_bit_set_array :: proc(fi: ^Info, v: any, type_info: ^runtime.Type_Info, name: string, verb: rune) {
info := type_info.variant.(runtime.Type_Info_Bit_Set)
bit_size := int(8*type_info.size)
et := runtime.type_info_base(info.elem)
e, is_enum := et.variant.(runtime.Type_Info_Enum)
if verb != 'w' {
if name != "" {
io.write_string(fi.writer, name, &fi.n)
} else {
reflect.write_type(fi.writer, type_info, &fi.n)
}
}
io.write_byte(fi.writer, '{', &fi.n)
defer io.write_byte(fi.writer, '}', &fi.n)
bytes := ([^]u8)(v.data)
commas := 0
loop: for bit_index in 0..<bit_size {
if (bytes[bit_index/8] >> uint(bit_index & 7)) & 1 == 0 {
continue
}
i := i64(bit_index) + info.lower
if commas > 0 {
io.write_string(fi.writer, ", ", &fi.n)
}
if is_enum {
enum_name: string
if ti_named, is_named := info.elem.variant.(runtime.Type_Info_Named); is_named {
enum_name = ti_named.name
}
for ev, evi in e.values {
if u64(ev) == u64(i) {
if verb == 'w' {
io.write_string(fi.writer, enum_name, &fi.n)
io.write_byte(fi.writer, '.', &fi.n)
}
io.write_string(fi.writer, e.names[evi], &fi.n)
commas += 1
continue loop
}
}
}
io.write_i64(fi.writer, i, 10, &fi.n)
commas += 1
}
}
// Writes the specified number of indents to the provided Info structure
//
// Inputs:
-4
View File
@@ -11707,10 +11707,6 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
field_count = et->Enum.fields.count;
}
if (is_type_array(bit_set_to_int(t))) {
is_constant = false;
}
for (Ast *elem : cl->elems) {
if (elem->kind == Ast_FieldValue) {
error(elem, "'field = value' in a bit_set literal is not allowed");
+5 -8
View File
@@ -1384,14 +1384,11 @@ gb_internal void check_bit_set_type(CheckerContext *c, Type *type, Type *named_t
Type *t = default_type(lhs.type);
if (bs->underlying != nullptr) {
Type *u = check_type(c, bs->underlying);
// if (!is_valid_bit_field_backing_type(u)) {
if (!is_type_integer(u)) {
if (!is_valid_bit_field_backing_type(u)) {
gbString ts = type_to_string(u);
error(bs->underlying, "Expected an underlying integer for the bit set, got %s", ts);
error(bs->underlying, "Expected an underlying integer or array of integers for the bit set, got %s", ts);
gb_string_free(ts);
if (!is_valid_bit_field_backing_type(u)) {
return;
}
return;
}
type->BitSet.underlying = u;
}
@@ -1502,9 +1499,9 @@ gb_internal void check_bit_set_type(CheckerContext *c, Type *type, Type *named_t
i64 bits = MAX_BITS
; if (bs->underlying != nullptr) {
Type *u = check_type(c, bs->underlying);
if (!is_type_integer(u)) {
if (!is_valid_bit_field_backing_type(u)) {
gbString ts = type_to_string(u);
error(bs->underlying, "Expected an underlying integer for the bit set, got %s", ts);
error(bs->underlying, "Expected an underlying integer or array of integers for the bit set, got %s", ts);
gb_string_free(ts);
return;
}
+39 -1
View File
@@ -500,7 +500,45 @@ gb_internal LLVMValueRef lb_big_int_to_llvm(lbModule *m, Type *original_type, Bi
if (big_int_is_zero(a)) {
return LLVMConstNull(lb_type(m, original_type));
}
// NOTE(bill): a bit_set backed by an array of integers (e.g. `bit_set[E; [4]u64]`) is represented as an LLVM array.
// There for its constant value is the same bit mask as an integer-backed bit_set, split into element-sized
// little-endian chunks (element `i` holds bits `[i*elem_bits, (i+1)*elem_bits)`), which matches how membership
// and literals index into the array.
if (is_type_bit_set(original_type)) {
Type *backing = bit_set_to_int(original_type);
if (is_type_array(backing)) {
Type *elem = backing->Array.elem;
i64 n = backing->Array.count;
i64 elem_bits = 8*type_size_of(elem);
BigInt v = {};
big_int_init(&v, a);
defer (big_int_dealloc(&v));
BigInt shift = {};
big_int_from_u64(&shift, cast(u64)elem_bits);
defer (big_int_dealloc(&shift));
BigInt mask = {}; // (1 << elem_bits) - 1
big_int_from_u64(&mask, 1);
big_int_shl(&mask, &mask, &shift);
mp_decr(&mask);
defer (big_int_dealloc(&mask));
LLVMTypeRef elem_llvm = lb_type(m, elem);
LLVMValueRef *elems = gb_alloc_array(temporary_allocator(), LLVMValueRef, cast(isize)n);
for (i64 i = 0; i < n; i++) {
BigInt chunk = {};
big_int_and(&chunk, &v, &mask);
elems[i] = lb_big_int_to_llvm(m, elem, &chunk);
big_int_dealloc(&chunk);
big_int_shr_eq(&v, &shift);
}
return LLVMConstArray(elem_llvm, elems, cast(unsigned)n);
}
}
BigInt val = {};
big_int_init(&val, a);
+120 -7
View File
@@ -187,6 +187,10 @@ gb_internal lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x,
if (is_type_bit_set(x.type)) {
ExactValue ev_mask = exact_bit_set_all_set_mask(x.type);
lbValue mask = lb_const_value(p->module, x.type, ev_mask);
if (is_type_array(bit_set_to_int(x.type))) {
lbValue res = lb_emit_arith(p, Token_Xor, x, mask, x.type);
return lb_emit_conv(p, res, type);
}
cmp.value = LLVMBuildXor(p->builder, x.value, mask.value, "");
} else {
cmp.value = LLVMBuildNot(p->builder, x.value, "");
@@ -2116,6 +2120,36 @@ gb_internal lbValue lb_build_binary_in(lbProcedure *p, lbValue left, lbValue rig
Type *it = bit_set_to_int(rt);
if (is_type_array(it)) {
Type *elem = base_array_type(it);
i64 elem_bits = 8*type_size_of(elem);
lbValue key = lb_emit_conv(p, left, t_int);
lbValue lower_v = lb_const_int(p->module, t_int, rt->BitSet.lower);
key = lb_emit_arith(p, Token_Sub, key, lower_v, t_int);
lbValue ebits = lb_const_int(p->module, t_int, elem_bits);
lbValue elem_idx = lb_emit_arith(p, Token_Quo, key, ebits, t_int);
lbValue bit_idx = lb_emit_arith(p, Token_Mod, key, ebits, t_int);
lbValue arr = lb_emit_transmute(p, right, it);
lbValue arr_ptr = lb_address_from_load_or_generate_local(p, arr);
arr_ptr.type = alloc_type_pointer(it);
lbValue e_ptr = lb_emit_array_ep(p, arr_ptr, elem_idx);
lbValue e_val = lb_emit_load(p, e_ptr);
lbValue shifted = lb_emit_arith(p, Token_Shr, e_val, lb_emit_conv(p, bit_idx, elem), elem);
lbValue masked = lb_emit_arith(p, Token_And, shifted, lb_const_int(p->module, elem, 1), elem);
lbValue zero = lb_const_int(p->module, elem, 0);
if (op == Token_in) {
return lb_emit_conv(p, lb_emit_comp(p, Token_NotEq, masked, zero), t_bool);
} else {
return lb_emit_conv(p, lb_emit_comp(p, Token_CmpEq, masked, zero), t_bool);
}
}
left = lb_emit_conv(p, left, it);
if (is_type_different_to_arch_endianness(it)) {
left = lb_emit_byte_swap(p, left, integer_endian_type_to_platform_type(it));
@@ -3429,6 +3463,29 @@ gb_internal lbValue lb_compare_records(lbProcedure *p, TokenKind op_kind, lbValu
gb_internal lbValue lb_bit_set_array_is_zero(lbProcedure *p, lbValue arr) {
Type *at = base_type(arr.type);
GB_ASSERT(at->kind == Type_Array);
Type *elem = at->Array.elem;
i64 n = at->Array.count;
lbValue acc = {};
acc.type = elem;
acc.value = LLVMBuildExtractValue(p->builder, arr.value, 0, "");
for (i64 i = 1; i < n; i++) {
lbValue e = {};
e.type = elem;
e.value = LLVMBuildExtractValue(p->builder, arr.value, cast(unsigned)i, "");
acc = lb_emit_arith(p, Token_Or, acc, e, elem);
}
lbValue res = {};
res.type = t_llvm_bool;
res.value = LLVMBuildICmp(p->builder, LLVMIntEQ, acc.value, lb_const_int(p->module, elem, 0).value, "");
return res;
}
gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right) {
Type *a = core_type(left.type);
Type *b = core_type(right.type);
@@ -3753,6 +3810,52 @@ gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left
}
if (is_type_bit_set(a)) {
Type *it_backing = bit_set_to_int(a);
if (is_type_array(it_backing)) {
lbValue lhs = lb_emit_transmute(p, left, it_backing);
lbValue rhs = lb_emit_transmute(p, right, it_backing);
switch (op_kind) {
case Token_CmpEq:
case Token_NotEq:
{
lbValue diff = lb_emit_arith(p, Token_Xor, lhs, rhs, it_backing);
lbValue equal = lb_bit_set_array_is_zero(p, diff);
if (op_kind == Token_NotEq) {
lbValue res = {};
res.type = t_llvm_bool;
res.value = LLVMBuildNot(p->builder, equal.value, "");
return res;
}
return equal;
}
case Token_Lt:
case Token_LtEq:
{
// subset: (lhs &~ rhs) == {}; strict also requires lhs != rhs
lbValue d = lb_emit_arith(p, Token_AndNot, lhs, rhs, it_backing);
lbValue res = lb_bit_set_array_is_zero(p, d);
if (op_kind == Token_Lt) {
lbValue diff = lb_emit_arith(p, Token_Xor, lhs, rhs, it_backing);
lbValue equal = lb_bit_set_array_is_zero(p, diff);
res = lb_emit_arith(p, Token_AndNot, res, equal, t_llvm_bool);
}
return res;
}
case Token_Gt:
case Token_GtEq:
{
// superset: (rhs &~ lhs) == {}; strict also requires lhs != rhs
lbValue d = lb_emit_arith(p, Token_AndNot, rhs, lhs, it_backing);
lbValue res = lb_bit_set_array_is_zero(p, d);
if (op_kind == Token_Gt) {
lbValue diff = lb_emit_arith(p, Token_Xor, lhs, rhs, it_backing);
lbValue equal = lb_bit_set_array_is_zero(p, diff);
res = lb_emit_arith(p, Token_AndNot, res, equal, t_llvm_bool);
}
return res;
}
}
}
switch (op_kind) {
case Token_Lt:
case Token_LtEq:
@@ -6461,20 +6564,30 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
Type *backing = bit_set_to_int(type);
if (is_type_array(backing)) {
GB_PANIC("TODO: bit_set [N]T");
Type *base_it = core_array_type(backing);
i64 bits_per_elem = 8*type_size_of(base_it);
gb_unused(bits_per_elem);
lbValue one = lb_const_value(p->module, t_i64, exact_value_i64(1));
lbValue ebits = lb_const_value(p->module, t_int, exact_value_i64(bits_per_elem));
lbValue one = lb_const_value(p->module, base_it, exact_value_i64(1));
lbValue arr_ptr = lb_addr_get_ptr(p, v);
arr_ptr.type = alloc_type_pointer(backing);
for (Ast *elem : cl->elems) {
GB_ASSERT(elem->kind != Ast_FieldValue);
lbValue expr = lb_build_expr(p, elem);
GB_ASSERT(expr.type->kind != Type_Tuple);
lbValue e = lb_emit_conv(p, expr, t_i64);
e = lb_emit_arith(p, Token_Sub, e, lower, t_i64);
// lbValue idx = lb_emit_arith(p, Token_Div, e, bits_per_elem, t_i64);
// lbValue val = lb_emit_arith(p, Token_Div, e, bits_per_elem, t_i64);
lbValue e = lb_emit_conv(p, expr, t_int);
e = lb_emit_arith(p, Token_Sub, e, lower, t_int);
lbValue elem_idx = lb_emit_arith(p, Token_Quo, e, ebits, t_int);
lbValue bit_idx = lb_emit_arith(p, Token_Mod, e, ebits, t_int);
lbValue e_ptr = lb_emit_array_ep(p, arr_ptr, elem_idx);
lbValue bit = lb_emit_arith(p, Token_Shl, one, lb_emit_conv(p, bit_idx, base_it), base_it);
lbValue old_value = lb_emit_load(p, e_ptr);
lbValue new_value = lb_emit_arith(p, Token_Or, old_value, bit, base_it);
lb_emit_store(p, e_ptr, new_value);
}
} else {
Type *it = bit_set_to_int(bt);