Browse Source

Merge pull request #4611 from tf2spi/4491-max-field-align-pack

Add packing + aligned access w/ field_align
gingerBill 7 months ago
parent
commit
a2a0a2c8d8
3 changed files with 35 additions and 4 deletions
  1. 2 0
      src/llvm_backend.hpp
  2. 17 1
      src/llvm_backend_general.cpp
  3. 16 3
      src/llvm_backend_utility.cpp

+ 2 - 0
src/llvm_backend.hpp

@@ -747,3 +747,5 @@ gb_global char const *llvm_linkage_strings[] = {
 };
 
 #define ODIN_METADATA_IS_PACKED str_lit("odin-is-packed")
+#define ODIN_METADATA_MIN_ALIGN str_lit("odin-min-align")
+#define ODIN_METADATA_MAX_ALIGN str_lit("odin-max-align")

+ 17 - 1
src/llvm_backend_general.cpp

@@ -734,6 +734,17 @@ gb_internal LLVMValueRef OdinLLVMBuildLoad(lbProcedure *p, LLVMTypeRef type, LLV
 		if (is_packed != 0) {
 			LLVMSetAlignment(result, 1);
 		}
+		u64 align = LLVMGetAlignment(result);
+		u64 align_min = lb_get_metadata_custom_u64(p->module, value, ODIN_METADATA_MIN_ALIGN);
+		u64 align_max = lb_get_metadata_custom_u64(p->module, value, ODIN_METADATA_MAX_ALIGN);
+		if (align_min != 0 && align < align_min) {
+			align = align_min;
+		}
+		if (align_max != 0 && align > align_max) {
+			align = align_max;
+		}
+		GB_ASSERT(align <= UINT_MAX);
+		LLVMSetAlignment(result, (unsigned int)align);
 	}
 
 	return result;
@@ -2121,6 +2132,7 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
 			}
 			
 			i64 prev_offset = 0;
+			bool requires_packing = type->Struct.is_packed;
 			for (i32 field_index : struct_fields_index_by_increasing_offset(temporary_allocator(), type)) {
 				Entity *field = type->Struct.fields[field_index];
 				i64 offset = type->Struct.offsets[field_index];
@@ -2141,6 +2153,10 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
 					field_type = t_rawptr;
 				}
 
+				// max_field_align might misalign items in a way that requires packing
+				// so check the alignment of all fields to see if packing is required.
+				requires_packing = requires_packing || ((offset % type_align_of(field_type)) != 0);
+
 				array_add(&fields, lb_type(m, field_type));
 
 				prev_offset = offset + type_size_of(field->type);
@@ -2155,7 +2171,7 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
 				GB_ASSERT(fields[i] != nullptr);
 			}
 			
-			LLVMTypeRef struct_type = LLVMStructTypeInContext(ctx, fields.data, cast(unsigned)fields.count, type->Struct.is_packed);
+			LLVMTypeRef struct_type = LLVMStructTypeInContext(ctx, fields.data, cast(unsigned)fields.count, requires_packing);
 			map_set(&m->struct_field_remapping, cast(void *)struct_type, field_remapping);
 			map_set(&m->struct_field_remapping, cast(void *)type, field_remapping);			
 			#if 0

+ 16 - 3
src/llvm_backend_utility.cpp

@@ -1200,9 +1200,22 @@ gb_internal lbValue lb_emit_struct_ep(lbProcedure *p, lbValue s, i32 index) {
 	lbValue gep = lb_emit_struct_ep_internal(p, s, index, result_type);
 
 	Type *bt = base_type(t);
-	if (bt->kind == Type_Struct && bt->Struct.is_packed) {
-		lb_set_metadata_custom_u64(p->module, gep.value, ODIN_METADATA_IS_PACKED, 1);
-		GB_ASSERT(lb_get_metadata_custom_u64(p->module, gep.value, ODIN_METADATA_IS_PACKED) == 1);
+	if (bt->kind == Type_Struct) {
+		if (bt->Struct.is_packed) {
+			lb_set_metadata_custom_u64(p->module, gep.value, ODIN_METADATA_IS_PACKED, 1);
+			GB_ASSERT(lb_get_metadata_custom_u64(p->module, gep.value, ODIN_METADATA_IS_PACKED) == 1);
+		}
+		u64 align_max = bt->Struct.custom_max_field_align;
+		u64 align_min = bt->Struct.custom_min_field_align;
+		GB_ASSERT(align_min == 0 || align_max == 0 || align_min <= align_max);
+		if (align_max) {
+			lb_set_metadata_custom_u64(p->module, gep.value, ODIN_METADATA_MAX_ALIGN, align_max);
+			GB_ASSERT(lb_get_metadata_custom_u64(p->module, gep.value, ODIN_METADATA_MAX_ALIGN) == align_max);
+		}
+		if (align_min) {
+			lb_set_metadata_custom_u64(p->module, gep.value, ODIN_METADATA_MIN_ALIGN, align_min);
+			GB_ASSERT(lb_get_metadata_custom_u64(p->module, gep.value, ODIN_METADATA_MIN_ALIGN) == align_min);
+		}
 	}
 
 	return gep;