diff options
| author | gingerBill <bill@gingerbill.org> | 2024-04-24 14:46:34 +0100 |
|---|---|---|
| committer | gingerBill <bill@gingerbill.org> | 2024-04-24 14:46:34 +0100 |
| commit | c330e5b5c1b512e1b0ca7181941057e5f2e085e4 (patch) | |
| tree | f7e351bf4bbee729ff2fbe68258114a726151335 /src/llvm_backend_expr.cpp | |
| parent | ec5a84a5379236a2413b8f3115509629879f5b53 (diff) | |
Improve codegen for `bit_field` compound literals with an integer backing
Diffstat (limited to 'src/llvm_backend_expr.cpp')
| -rw-r--r-- | src/llvm_backend_expr.cpp | 111 |
1 files changed, 97 insertions, 14 deletions
diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index edd5daeca..4209ba1ea 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -4296,7 +4296,19 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) { switch (bt->kind) { default: GB_PANIC("Unknown CompoundLit type: %s", type_to_string(type)); break; - case Type_BitField: + case Type_BitField: { + TEMPORARY_ALLOCATOR_GUARD(); + + // Type *backing_type = core_type(bt->BitField.backing_type); + + struct FieldData { + Type *field_type; + u64 bit_offset; + u64 bit_size; + }; + auto values = array_make<lbValue>(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; @@ -4307,26 +4319,97 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) { GB_ASSERT(sel.entity != nullptr); i64 index = sel.index[0]; - i64 bit_offset = 0; - i64 bit_size = -1; - for_array(i, bt->BitField.fields) { - Entity *f = bt->BitField.fields[i]; - if (f == sel.entity) { - bit_offset = bt->BitField.bit_offsets[i]; - bit_size = bt->BitField.bit_sizes[i]; - break; - } - } + 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; lbValue field_expr = lb_build_expr(p, fv->value); field_expr = lb_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; + } + } + + if (fields.count == bt->BitField.fields.count) { + Type *backing_type = core_type(bt->BitField.backing_type); + 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 + + lbValue dst_byte_ptr = lb_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)) { + LLVMTypeRef lbt = lb_type(p->module, backing_type); + + LLVMValueRef res = LLVMConstInt(lbt, 0, false); + + for (isize i = 0; i < fields.count; i++) { + auto const &f = fields[i]; + + LLVMValueRef mask = LLVMConstInt(lbt, 1, false); + mask = LLVMConstShl(mask, LLVMConstInt(lbt, f.bit_size, false)); + mask = LLVMConstSub(mask, LLVMConstInt(lbt, 1, false)); - lbAddr field_addr = lb_addr_bit_field(v.addr, field_type, index, bit_offset, bit_size); - lb_addr_store(p, field_addr, field_expr); + LLVMValueRef elem = values[i].value; + elem = LLVMBuildZExt(p->builder, elem, lbt, ""); + elem = LLVMBuildAnd(p->builder, elem, mask, ""); + + elem = LLVMBuildShl(p->builder, elem, LLVMConstInt(lbt, f.bit_offset, false), ""); + + res = LLVMBuildOr(p->builder, res, elem, ""); + } + LLVMBuildStore(p->builder, res, v.addr.value); + } else { + 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; + } + lbValue dst = lb_emit_ptr_offset(p, dst_byte_ptr, lb_const_int(p->module, t_int, f.bit_offset/8)); + lbValue src = lb_address_from_load_or_generate_local(p, values[i]); + lb_mem_copy_non_overlapping(p, dst, src, lb_const_int(p->module, t_uintptr, byte_size)); + } else { + lbAddr dst = lb_addr_bit_field(v.addr, f.field_type, f.bit_offset, f.bit_size); + lb_addr_store(p, dst, values[i]); + } + } + } + } else { + // individual storing + for_array(i, values) { + auto const &f = fields[i]; + lbAddr dst = lb_addr_bit_field(v.addr, f.field_type, f.bit_offset, f.bit_size); + lb_addr_store(p, dst, values[i]); + } } + return v; + } case Type_Struct: { // TODO(bill): "constant" '#raw_union's are not initialized constantly at the moment. @@ -4771,7 +4854,7 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) { u8 bit_size = bf_type->BitField.bit_sizes[index]; i64 bit_offset = bf_type->BitField.bit_offsets[index]; - return lb_addr_bit_field(ptr, f->type, index, bit_offset, bit_size); + return lb_addr_bit_field(ptr, f->type, bit_offset, bit_size); } { |