diff options
| author | gingerBill <bill@gingerbill.org> | 2024-04-24 17:01:09 +0100 |
|---|---|---|
| committer | gingerBill <bill@gingerbill.org> | 2024-04-24 17:01:09 +0100 |
| commit | 214537b4209211f9ceb9932b8d9980ea6503e8ea (patch) | |
| tree | 8ccecfad8be31642407683958ffa826682a7a061 /src/llvm_backend_expr.cpp | |
| parent | c330e5b5c1b512e1b0ca7181941057e5f2e085e4 (diff) | |
Improve codegen for `bit_field [N]T` compound literals
Diffstat (limited to 'src/llvm_backend_expr.cpp')
| -rw-r--r-- | src/llvm_backend_expr.cpp | 91 |
1 files changed, 83 insertions, 8 deletions
diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 4209ba1ea..ee1a384ae 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -4347,7 +4347,19 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) { } } - if (fields.count == bt->BitField.fields.count) { + 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; + } + } + + if (!any_fields_different_endian && + fields.count == bt->BitField.fields.count) { + // SINGLE INTEGER BACKING ONLY + 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))); @@ -4359,27 +4371,90 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) { 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); + LLVMTypeRef lit = lb_type(p->module, backing_type); - LLVMValueRef res = LLVMConstInt(lbt, 0, false); + LLVMValueRef res = LLVMConstInt(lit, 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)); + LLVMValueRef mask = LLVMConstInt(lit, 1, false); + mask = LLVMConstShl(mask, LLVMConstInt(lit, f.bit_size, false)); + mask = LLVMConstSub(mask, LLVMConstInt(lit, 1, false)); LLVMValueRef elem = values[i].value; - elem = LLVMBuildZExt(p->builder, elem, lbt, ""); + elem = LLVMBuildZExt(p->builder, elem, lit, ""); elem = LLVMBuildAnd(p->builder, elem, mask, ""); - elem = LLVMBuildShl(p->builder, elem, LLVMConstInt(lbt, f.bit_offset, false), ""); + elem = LLVMBuildShl(p->builder, elem, LLVMConstInt(lit, f.bit_offset, false), ""); res = LLVMBuildOr(p->builder, res, elem, ""); } + LLVMBuildStore(p->builder, res, v.addr.value); + } else if (is_type_array(backing_type)) { + // ARRAY OF INTEGER BACKING + + i64 array_count = backing_type->Array.count; + LLVMTypeRef lit = lb_type(p->module, core_type(backing_type->Array.elem)); + gb_unused(array_count); + gb_unused(lit); + + LLVMValueRef *elems = gb_alloc_array(temporary_allocator(), LLVMValueRef, array_count); + for (i64 i = 0; i < array_count; i++) { + elems[i] = LLVMConstInt(lit, 0, false); + } + + 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]; + + LLVMValueRef val = values[i].value; + LLVMTypeRef vt = lb_type(p->module, values[i].type); + 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; + + LLVMValueRef mask = LLVMConstInt(vt, 1, false); + mask = LLVMConstShl(mask, LLVMConstInt(vt, mask_width, false)); + mask = LLVMConstSub(mask, LLVMConstInt(vt, 1, false)); + + LLVMValueRef to_set = LLVMBuildAnd(p->builder, val, mask, ""); + + if (elem_bit_offset != 0) { + to_set = LLVMBuildShl(p->builder, to_set, LLVMConstInt(vt, elem_bit_offset, false), ""); + } + to_set = LLVMBuildTrunc(p->builder, to_set, lit, ""); + + if (LLVMIsNull(elems[elem_idx])) { + elems[elem_idx] = to_set; // don't even bother doing `0 | to_set` + } else { + elems[elem_idx] = LLVMBuildOr(p->builder, elems[elem_idx], to_set, ""); + } + + if (mask_width != 0) { + val = LLVMBuildLShr(p->builder, val, LLVMConstInt(vt, mask_width, false), ""); + } + curr_bit_offset += mask_width; + } + + GB_ASSERT(curr_bit_offset == f.bit_offset + f.bit_size); + } + + for (i64 i = 0; i < array_count; i++) { + LLVMValueRef elem_ptr = LLVMBuildStructGEP2(p->builder, lb_type(p->module, backing_type), v.addr.value, cast(unsigned)i, ""); + LLVMBuildStore(p->builder, elems[i], elem_ptr); + } } else { + // SLOW STORAGE + for_array(i, fields) { auto const &f = fields[i]; |