Begin mocking out bit_field operations

This commit is contained in:
gingerBill
2024-04-30 15:36:26 +01:00
parent 915db6d228
commit f69b127c9f
4 changed files with 328 additions and 2 deletions

View File

@@ -235,6 +235,19 @@ gb_internal cgAddr cg_addr_soa_variable(cgValue addr, cgValue index, Ast *index_
}
gb_internal cgAddr cg_addr_bit_field(cgValue addr, Type *type, i64 bit_offset, i64 bit_size) {
GB_ASSERT(is_type_pointer(addr.type));
Type *mt = type_deref(addr.type);
GB_ASSERT_MSG(is_type_bit_field(mt), "%s", type_to_string(mt));
cgAddr v = {cgAddr_BitField, addr};
v.bitfield.type = type;
v.bitfield.bit_offset = bit_offset;
v.bitfield.bit_size = bit_size;
return v;
}
gb_internal void cg_set_debug_pos_from_node(cgProcedure *p, Ast *node) {
if (node) {

View File

@@ -66,6 +66,8 @@ enum cgAddrKind {
cgAddr_Swizzle,
cgAddr_SwizzleLarge,
cgAddr_BitField,
};
struct cgAddr {
@@ -100,6 +102,11 @@ struct cgAddr {
Type *type;
Slice<i32> indices;
} swizzle_large;
struct {
Type *type;
i64 bit_offset;
i64 bit_size;
} bitfield;
};
};

View File

@@ -2701,6 +2701,195 @@ cgAddr cg_build_addr_compound_lit(cgProcedure *p, Ast *expr) {
switch (bt->kind) {
default: GB_PANIC("Unknown CompoundLit type: %s", type_to_string(type)); break;
case Type_BitField: {
TEMPORARY_ALLOCATOR_GUARD();
struct FieldData {
Type *field_type;
u64 bit_offset;
u64 bit_size;
};
auto values = array_make<cgValue> (temporary_allocator(), 0, cl->elems.count);
auto fields = array_make<FieldData>(temporary_allocator(), 0, cl->elems.count);
for (Ast *elem : cl->elems) {
ast_node(fv, FieldValue, elem);
String name = fv->field->Ident.token.string;
Selection sel = lookup_field(bt, name, false);
GB_ASSERT(sel.is_bit_field);
GB_ASSERT(!sel.indirect);
GB_ASSERT(sel.index.count == 1);
GB_ASSERT(sel.entity != nullptr);
i64 index = sel.index[0];
Entity *f = bt->BitField.fields[index];
GB_ASSERT(f == sel.entity);
i64 bit_offset = bt->BitField.bit_offsets[index];
i64 bit_size = bt->BitField.bit_sizes[index];
GB_ASSERT(bit_size > 0);
Type *field_type = sel.entity->type;
cgValue field_expr = cg_build_expr(p, fv->value);
field_expr = cg_emit_conv(p, field_expr, field_type);
array_add(&values, field_expr);
array_add(&fields, FieldData{field_type, cast(u64)bit_offset, cast(u64)bit_size});
}
// NOTE(bill): inline insertion sort should be good enough, right?
for (isize i = 1; i < values.count; i++) {
for (isize j = i;
j > 0 && fields[i].bit_offset < fields[j].bit_offset;
j--) {
auto vtmp = values[j];
values[j] = values[j-1];
values[j-1] = vtmp;
auto ftmp = fields[j];
fields[j] = fields[j-1];
fields[j-1] = ftmp;
}
}
bool any_fields_different_endian = false;
for (auto const &f : fields) {
if (is_type_different_to_arch_endianness(f.field_type)) {
// NOTE(bill): Just be slow for this, to be correct
any_fields_different_endian = true;
break;
}
}
Type *backing_type = core_type(bt->BitField.backing_type);
GB_ASSERT(!is_type_integer_128bit(core_array_type(backing_type)));
if (!any_fields_different_endian &&
fields.count == bt->BitField.fields.count) {
// SINGLE INTEGER BACKING ONLY
GB_ASSERT(is_type_integer(backing_type) ||
(is_type_array(backing_type) && is_type_integer(backing_type->Array.elem)));
// NOTE(bill): all fields are present
// this means no masking is necessary since on write, the bits will be overridden
cgValue dst_byte_ptr = cg_emit_conv(p, v.addr, t_u8_ptr);
u64 total_bit_size = cast(u64)(8*type_size_of(bt));
if (is_type_integer(backing_type)) {
TB_DataType dt = cg_data_type(backing_type);
cgValue res = cg_const_int(p, backing_type, 0);
for (isize i = 0; i < fields.count; i++) {
auto const &f = fields[i];
// IMPORTANT NOTE(bill): this will not work for 128-bit integers
u64 mask = (1ull<<f.bit_size)-1;
cgValue elem = cg_flatten_value(p, values[i]);
GB_ASSERT(elem.kind == cgValue_Value);
elem.node = tb_inst_zxt(p->func, elem.node, dt);
elem.node = tb_inst_and(p->func, elem.node, tb_inst_uint(p->func, dt, mask));
elem.node = tb_inst_shl(p->func, elem.node, tb_inst_uint(p->func, dt, f.bit_offset), TB_ARITHMATIC_NONE);
res.node = tb_inst_or(p->func, res.node, elem.node);
}
cg_addr_store(p, v, res);
} else if (is_type_array(backing_type)) {
// ARRAY OF INTEGER BACKING
i64 array_count = backing_type->Array.count;
TB_DataType lit = cg_data_type(core_type(backing_type->Array.elem));
TB_Node **elems = gb_alloc_array(temporary_allocator(), TB_Node *, array_count);
for (i64 i = 0; i < array_count; i++) {
elems[i] = tb_inst_uint(p->func, lit, 0);
}
u64 elem_bit_size = cast(u64)(8*type_size_of(backing_type->Array.elem));
u64 curr_bit_offset = 0;
for (isize i = 0; i < fields.count; i++) {
auto const &f = fields[i];
cgValue val = values[i];
Type *vt = val.type;
TB_DataType dt = cg_data_type(vt);
for (u64 bits_to_set = f.bit_size;
bits_to_set > 0;
/**/) {
i64 elem_idx = curr_bit_offset/elem_bit_size;
u64 elem_bit_offset = curr_bit_offset%elem_bit_size;
u64 mask_width = gb_min(bits_to_set, elem_bit_size-elem_bit_offset);
GB_ASSERT(mask_width > 0);
bits_to_set -= mask_width;
// IMPORTANT NOTE(bill): this will not work for 128-bit integers
u64 mask = (1ull<<mask_width)-1;
TB_Node *to_set = tb_inst_and(p->func, val.node, tb_inst_uint(p->func, dt, mask));
if (elem_bit_offset != 0) {
to_set = tb_inst_shl(p->func, to_set, tb_inst_uint(p->func, dt, elem_bit_offset), TB_ARITHMATIC_NONE);
}
to_set = tb_inst_trunc(p->func, to_set, lit);
if (tb_node_is_constant_zero(elems[elem_idx])) {
elems[elem_idx] = to_set; // don't even bother doing `0 | to_set`
} else {
elems[elem_idx] = tb_inst_or(p->func, elems[elem_idx], to_set);
}
if (mask_width != 0) {
val.node = tb_inst_shr(p->func, val.node, tb_inst_uint(p->func, dt, mask_width));
}
curr_bit_offset += mask_width;
}
GB_ASSERT(curr_bit_offset == f.bit_offset + f.bit_size);
}
for (i64 i = 0; i < array_count; i++) {
cgValue elem_ptr = cg_flatten_value(p, cg_emit_struct_ep(p, v.addr, i));
GB_ASSERT(elem_ptr.kind == cgValue_Value);
cg_emit_store(p, elem_ptr, cg_value(elems[i], type_deref(elem_ptr.type)));
}
} else {
// SLOW STORAGE
for_array(i, fields) {
auto const &f = fields[i];
if ((f.bit_offset & 7) == 0) {
u64 unpacked_bit_size = cast(u64)(8*type_size_of(f.field_type));
u64 byte_size = (f.bit_size+7)/8;
if (f.bit_offset + unpacked_bit_size <= total_bit_size) {
byte_size = unpacked_bit_size/8;
}
cgValue dst = cg_emit_ptr_offset(p, dst_byte_ptr, cg_const_int(p, t_int, f.bit_offset/8));
cgValue src = cg_address_from_load_or_generate_local(p, values[i]);
cg_builtin_mem_copy_non_overlapping(p, dst, src, cg_const_int(p, t_uintptr, byte_size));
} else {
cgAddr dst = cg_addr_bit_field(v.addr, f.field_type, f.bit_offset, f.bit_size);
cg_addr_store(p, dst, values[i]);
}
}
}
} else {
// individual storing
for_array(i, values) {
auto const &f = fields[i];
cgAddr dst = cg_addr_bit_field(v.addr, f.field_type, f.bit_offset, f.bit_size);
cg_addr_store(p, dst, values[i]);
}
}
return v;
}
case Type_Struct: {
TypeStruct *st = &bt->Struct;
cgValue comp_lit_ptr = cg_addr_get_ptr(p, v);
@@ -2974,7 +3163,7 @@ cgAddr cg_build_addr_compound_lit(cgProcedure *p, Ast *expr) {
// for (auto const &td : temp_data) if (td.value.node != nullptr) {
// if (td.elem_length > 0) {
// for (i64 k = 0; k < td.elem_length; k++) {
// LLVMValueRef index = cg_const_int(p->module, t_u32, td.elem_index + k).value;
// LLVMValueRef index = cg_const_int(p, t_u32, td.elem_index + k).value;
// vector_value.value = LLVMBuildInsertElement(p->builder, vector_value.value, td.value.value, index, "");
// }
// } else {

View File

@@ -228,6 +228,86 @@ gb_internal cgValue cg_addr_load(cgProcedure *p, cgAddr addr) {
case cgAddr_Default:
return cg_emit_load(p, addr.addr);
case cgAddr_BitField:
{
Type *ct = core_type(addr.bitfield.type);
bool do_mask = false;
if (is_type_unsigned(ct) || is_type_boolean(ct)) {
// Mask
if (addr.bitfield.bit_size != 8*type_size_of(ct)) {
do_mask = true;
}
}
i64 total_bitfield_bit_size = 8*type_size_of(cg_addr_type(addr));
i64 dst_byte_size = type_size_of(addr.bitfield.type);
cgAddr dst = cg_add_local(p, addr.bitfield.type, nullptr, true);
cgValue src = addr.addr;
cgValue bit_offset = cg_const_int(p, t_uintptr, addr.bitfield.bit_offset);
cgValue bit_size = cg_const_int(p, t_uintptr, addr.bitfield.bit_size);
cgValue byte_offset = cg_const_int(p, t_uintptr, (addr.bitfield.bit_offset+7)/8);
cgValue byte_size = cg_const_int(p, t_uintptr, (addr.bitfield.bit_size+7)/8);
GB_ASSERT(type_size_of(addr.bitfield.type) >= ((addr.bitfield.bit_size+7)/8));
cgValue r = {};
if (is_type_endian_big(addr.bitfield.type)) {
auto args = slice_make<cgValue>(temporary_allocator(), 4);
args[0] = dst.addr;
args[1] = src;
args[2] = bit_offset;
args[3] = bit_size;
cg_emit_runtime_call(p, "__read_bits", args);
cgValue shift_amount = cg_const_int(
p,
cg_addr_type(dst),
8*dst_byte_size - addr.bitfield.bit_size
);
r = cg_addr_load(p, dst);
r.node = tb_inst_shl(p->func, r.node, shift_amount.node, TB_ARITHMATIC_NONE);
} else if ((addr.bitfield.bit_offset % 8) == 0) {
cgValue copy_size = byte_size;
cgValue src_offset = cg_emit_conv(p, src, t_u8_ptr);
src_offset = cg_emit_ptr_offset(p, src_offset, byte_offset);
if (addr.bitfield.bit_offset + dst_byte_size <= total_bitfield_bit_size) {
do_mask = true;
copy_size = cg_const_int(p, t_uintptr, dst_byte_size);
}
cg_builtin_mem_copy_non_overlapping(p, dst.addr, src_offset, copy_size);
r = cg_addr_load(p, dst);
} else {
auto args = slice_make<cgValue>(temporary_allocator(), 4);
args[0] = dst.addr;
args[1] = src;
args[2] = bit_offset;
args[3] = bit_size;
cg_emit_runtime_call(p, "__read_bits", args);
r = cg_addr_load(p, dst);
}
Type *t = addr.bitfield.type;
if (do_mask) {
GB_ASSERT(addr.bitfield.bit_size < 8*type_size_of(ct));
cgValue mask = cg_const_int(p, t, (1ull<<cast(u64)addr.bitfield.bit_size)-1);
r = cg_emit_arith(p, Token_And, r, mask, t);
}
if (!is_type_unsigned(ct) && !is_type_boolean(ct)) {
// Sign extension
// m := 1<<(bit_size-1)
// r = (r XOR m) - m
cgValue m = cg_const_int(p, t, 1ull<<(addr.bitfield.bit_size-1));
r = cg_emit_arith(p, Token_Xor, r, m, t);
r = cg_emit_arith(p, Token_Sub, r, m, t);
}
return r;
}
case cgAddr_Map:
{
Type *map_type = base_type(type_deref(addr.addr.type));
@@ -337,7 +417,44 @@ gb_internal void cg_addr_store(cgProcedure *p, cgAddr addr, cgValue value) {
addr = cg_addr(cg_address_from_load(p, cg_addr_load(p, addr)));
}
if (addr.kind == cgAddr_RelativePointer) {
if (addr.kind == cgAddr_BitField) {
cgValue dst = addr.addr;
if (is_type_endian_big(addr.bitfield.type)) {
i64 shift_amount = 8*type_size_of(value.type) - addr.bitfield.bit_size;
cgValue shifted_value = value;
shifted_value.node = tb_inst_shr(p->func,
shifted_value.node,
tb_inst_uint(p->func, cg_data_type(shifted_value.type), shift_amount));
cgValue src = cg_address_from_load_or_generate_local(p, shifted_value);
auto args = slice_make<cgValue>(temporary_allocator(), 4);
args[0] = dst;
args[1] = src;
args[2] = cg_const_int(p, t_uintptr, addr.bitfield.bit_offset);
args[3] = cg_const_int(p, t_uintptr, addr.bitfield.bit_size);
cg_emit_runtime_call(p, "__write_bits", args);
} else if ((addr.bitfield.bit_offset % 8) == 0 &&
(addr.bitfield.bit_size % 8) == 0) {
cgValue src = cg_address_from_load_or_generate_local(p, value);
cgValue byte_offset = cg_const_int(p, t_uintptr, addr.bitfield.bit_offset/8);
cgValue byte_size = cg_const_int(p, t_uintptr, addr.bitfield.bit_size/8);
cgValue dst_offset = cg_emit_conv(p, dst, t_u8_ptr);
dst_offset = cg_emit_ptr_offset(p, dst_offset, byte_offset);
cg_builtin_mem_copy_non_overlapping(p, dst_offset, src, byte_size);
} else {
cgValue src = cg_address_from_load_or_generate_local(p, value);
auto args = slice_make<cgValue>(temporary_allocator(), 4);
args[0] = dst;
args[1] = src;
args[2] = cg_const_int(p, t_uintptr, addr.bitfield.bit_offset);
args[3] = cg_const_int(p, t_uintptr, addr.bitfield.bit_size);
cg_emit_runtime_call(p, "__write_bits", args);
}
return;
} else if (addr.kind == cgAddr_RelativePointer) {
GB_PANIC("TODO(bill): cgAddr_RelativePointer");
} else if (addr.kind == cgAddr_RelativeSlice) {
GB_PANIC("TODO(bill): cgAddr_RelativeSlice");