mirror of
https://github.com/odin-lang/Odin.git
synced 2026-10-08 13:51:37 -04:00
Support bit_set[E; [4]u64] style types
This commit is contained in:
1 parent
ae63a4a4c6
commit
b153d6071c
6 files changed
+236
-21
No files matched your search
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user