From fdf510b7b35119739d6e41170abd46204356d58b Mon Sep 17 00:00:00 2001 From: misomosi Date: Sat, 21 Dec 2024 19:59:31 -0500 Subject: [PATCH] Pack struct when needed, use field_align metadata --- src/llvm_backend.hpp | 2 ++ src/llvm_backend_general.cpp | 18 +++++++++++++++++- src/llvm_backend_utility.cpp | 19 ++++++++++++++++--- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index e84ffd1cdf8..3bbd97e4b6b 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -742,3 +742,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") diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index bab330da73d..76225625817 100644 --- a/src/llvm_backend_general.cpp +++ b/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 diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index a2a0ba4ccc7..b86d0773bc3 100644 --- a/src/llvm_backend_utility.cpp +++ b/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;