mirror of
https://github.com/odin-lang/Odin.git
synced 2026-08-29 08:21:32 +00:00
Begin mocking out bit_field operations
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user