Browse Source

Improve codegen for `bit_field [N]T` compound literals

gingerBill 1 year ago
parent
commit
214537b420
3 changed files with 91 additions and 18 deletions
  1. 4 6
      base/runtime/internal.odin
  2. 83 8
      src/llvm_backend_expr.cpp
  3. 4 4
      src/llvm_backend_general.cpp

+ 4 - 6
base/runtime/internal.odin

@@ -1042,19 +1042,17 @@ fixdfti :: proc(a: u64) -> i128 {
 __write_bits :: proc "contextless" (dst, src: [^]byte, offset: uintptr, size: uintptr) {
 	for i in 0..<size {
 		j := offset+i
-		the_bit := byte((src[i/8]) & (1<<(i&7)) != 0)
+		the_bit := byte((src[i>>3]) & (1<<(i&7)) != 0)
 		b := the_bit<<(j&7)
-		dst[j/8] &~= b
-		dst[j/8] |=  b
+		dst[j>>3] = (dst[j>>3] &~ b) | b
 	}
 }
 
 __read_bits :: proc "contextless" (dst, src: [^]byte, offset: uintptr, size: uintptr) {
 	for j in 0..<size {
 		i := offset+j
-		the_bit := byte((src[i/8]) & (1<<(i&7)) != 0)
+		the_bit := byte((src[i>>3]) & (1<<(i&7)) != 0)
 		b := the_bit<<(j&7)
-		dst[j/8] &~= b
-		dst[j/8] |=  b
+		dst[j>>3] = (dst[j>>3] &~ b) | b
 	}
 }

+ 83 - 8
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];
 

+ 4 - 4
src/llvm_backend_general.cpp

@@ -775,8 +775,8 @@ gb_internal void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) {
 		lbValue dst = addr.addr;
 		lbValue src = lb_address_from_load_or_generate_local(p, value);
 
-		if ((addr.bitfield.bit_offset & 7) == 0 &&
-		    (addr.bitfield.bit_size   & 7) == 0) {
+		if ((addr.bitfield.bit_offset % 8) == 0 &&
+		    (addr.bitfield.bit_size   % 8) == 0) {
 			lbValue byte_offset = lb_const_int(p->module, t_uintptr, addr.bitfield.bit_offset/8);
 			lbValue byte_size = lb_const_int(p->module, t_uintptr, addr.bitfield.bit_size/8);
 			lbValue dst_offset = lb_emit_conv(p, dst, t_u8_ptr);
@@ -1108,7 +1108,7 @@ gb_internal lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) {
 
 		i64 total_bitfield_bit_size = 8*type_size_of(lb_addr_type(addr));
 		i64 dst_byte_size = type_size_of(addr.bitfield.type);
-		lbAddr dst = lb_add_local_generated(p, addr.bitfield.type, false);
+		lbAddr dst = lb_add_local_generated(p, addr.bitfield.type, true);
 		lbValue src = addr.addr;
 
 		lbValue bit_offset  = lb_const_int(p->module, t_uintptr, addr.bitfield.bit_offset);
@@ -1118,7 +1118,7 @@ gb_internal lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) {
 
 		GB_ASSERT(type_size_of(addr.bitfield.type) >= ((addr.bitfield.bit_size+7)/8));
 
-		if ((addr.bitfield.bit_offset & 7) == 0) {
+		if ((addr.bitfield.bit_offset % 8) == 0) {
 			lbValue copy_size = byte_size;
 			lbValue src_offset = lb_emit_conv(p, src, t_u8_ptr);
 			src_offset = lb_emit_ptr_offset(p, src_offset, byte_offset);