Browse Source

Improve codegen for `bit_field` compound literals with an integer backing

gingerBill 1 year ago
parent
commit
c330e5b5c1
4 changed files with 101 additions and 23 deletions
  1. 0 1
      src/llvm_backend.hpp
  2. 97 14
      src/llvm_backend_expr.cpp
  3. 1 2
      src/llvm_backend_general.cpp
  4. 3 6
      src/llvm_backend_utility.cpp

+ 0 - 1
src/llvm_backend.hpp

@@ -122,7 +122,6 @@ struct lbAddr {
 		} swizzle_large;
 		} swizzle_large;
 		struct {
 		struct {
 			Type *type;
 			Type *type;
-			i64 index;
 			i64 bit_offset;
 			i64 bit_offset;
 			i64 bit_size;
 			i64 bit_size;
 		} bitfield;
 		} bitfield;

+ 97 - 14
src/llvm_backend_expr.cpp

@@ -4296,7 +4296,19 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
 	switch (bt->kind) {
 	switch (bt->kind) {
 	default: GB_PANIC("Unknown CompoundLit type: %s", type_to_string(type)); break;
 	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) {
 		for (Ast *elem : cl->elems) {
 			ast_node(fv, FieldValue, elem);
 			ast_node(fv, FieldValue, elem);
 			String name = fv->field->Ident.token.string;
 			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);
 			GB_ASSERT(sel.entity != nullptr);
 
 
 			i64 index = sel.index[0];
 			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);
 			GB_ASSERT(bit_size > 0);
 
 
 			Type *field_type = sel.entity->type;
 			Type *field_type = sel.entity->type;
 			lbValue field_expr = lb_build_expr(p, fv->value);
 			lbValue field_expr = lb_build_expr(p, fv->value);
 			field_expr = lb_emit_conv(p, field_expr, field_type);
 			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;
 		return v;
+	}
 
 
 	case Type_Struct: {
 	case Type_Struct: {
 		// TODO(bill): "constant" '#raw_union's are not initialized constantly at the moment.
 		// 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];
 				u8 bit_size = bf_type->BitField.bit_sizes[index];
 				i64 bit_offset = bf_type->BitField.bit_offsets[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);
 			}
 			}
 
 
 			{
 			{

+ 1 - 2
src/llvm_backend_general.cpp

@@ -450,14 +450,13 @@ gb_internal lbAddr lb_addr_swizzle_large(lbValue addr, Type *array_type, Slice<i
 	return v;
 	return v;
 }
 }
 
 
-gb_internal lbAddr lb_addr_bit_field(lbValue addr, Type *type, i64 index, i64 bit_offset, i64 bit_size) {
+gb_internal lbAddr lb_addr_bit_field(lbValue addr, Type *type, i64 bit_offset, i64 bit_size) {
 	GB_ASSERT(is_type_pointer(addr.type));
 	GB_ASSERT(is_type_pointer(addr.type));
 	Type *mt = type_deref(addr.type);
 	Type *mt = type_deref(addr.type);
 	GB_ASSERT_MSG(is_type_bit_field(mt), "%s", type_to_string(mt));
 	GB_ASSERT_MSG(is_type_bit_field(mt), "%s", type_to_string(mt));
 
 
 	lbAddr v = {lbAddr_BitField, addr};
 	lbAddr v = {lbAddr_BitField, addr};
 	v.bitfield.type       = type;
 	v.bitfield.type       = type;
-	v.bitfield.index      = index;
 	v.bitfield.bit_offset = bit_offset;
 	v.bitfield.bit_offset = bit_offset;
 	v.bitfield.bit_size   = bit_size;
 	v.bitfield.bit_size   = bit_size;
 	return v;
 	return v;

+ 3 - 6
src/llvm_backend_utility.cpp

@@ -97,15 +97,12 @@ gb_internal void lb_mem_zero_ptr(lbProcedure *p, LLVMValueRef ptr, Type *type, u
 	LLVMTypeRef llvm_type = lb_type(p->module, type);
 	LLVMTypeRef llvm_type = lb_type(p->module, type);
 
 
 	LLVMTypeKind kind = LLVMGetTypeKind(llvm_type);
 	LLVMTypeKind kind = LLVMGetTypeKind(llvm_type);
-
+	i64 sz = type_size_of(type);
 	switch (kind) {
 	switch (kind) {
 	case LLVMStructTypeKind:
 	case LLVMStructTypeKind:
 	case LLVMArrayTypeKind:
 	case LLVMArrayTypeKind:
-		{
-			// NOTE(bill): Enforce zeroing through memset to make sure padding is zeroed too
-			i32 sz = cast(i32)type_size_of(type);
-			lb_mem_zero_ptr_internal(p, ptr, lb_const_int(p->module, t_int, sz).value, alignment, false);
-		}
+		// NOTE(bill): Enforce zeroing through memset to make sure padding is zeroed too
+		lb_mem_zero_ptr_internal(p, ptr, lb_const_int(p->module, t_int, sz).value, alignment, false);
 		break;
 		break;
 	default:
 	default:
 		LLVMBuildStore(p->builder, LLVMConstNull(lb_type(p->module, type)), ptr);
 		LLVMBuildStore(p->builder, LLVMConstNull(lb_type(p->module, type)), ptr);