pack structs whose member alignment LLVM would raise above the target cap

arm64: return a narrow bare vector as itself, not coerced to an integer
This commit is contained in:
kalsprite
2026-08-13 21:52:34 -07:00
parent a50c9e0a1d
commit 98e580404a
2 changed files with 44 additions and 1 deletions

View File

@@ -347,6 +347,32 @@ gb_internal i64 lb_alignof(LLVMTypeRef type) {
return 1;
}
// The alignment LLVM itself will give the lowered type, which is not `lb_alignof`:
// that applies `max_simd_align`, and LLVM knows nothing about it. A 32-byte vector
// is 16-aligned on arm64 and Darwin and 32-aligned to LLVM, and a struct holding
// one has to be packed or LLVM re-inserts padding and moves the member.
gb_internal i64 lb_llvm_natural_alignof(LLVMTypeRef type) {
switch (LLVMGetTypeKind(type)) {
case LLVMStructTypeKind:
{
if (LLVMIsPackedStruct(type)) {
return 1;
}
unsigned field_count = LLVMCountStructElementTypes(type);
i64 max_align = 1;
for (unsigned i = 0; i < field_count; i++) {
max_align = gb_max(max_align, lb_llvm_natural_alignof(LLVMStructGetTypeAtIndex(type, i)));
}
return max_align;
}
case LLVMArrayTypeKind:
return lb_llvm_natural_alignof(OdinLLVMGetArrayElementType(type));
case LLVMVectorTypeKind:
return gb_max(next_pow2(lb_sizeof(type)), 1);
}
return lb_alignof(type);
}
#define LB_ABI_INFO(name) lbFunctionType *name(lbModule *m, LLVMTypeRef *arg_types, unsigned arg_count, LLVMTypeRef return_type, bool return_is_defined, bool return_is_tuple, ProcCallingConvention calling_convention, Type *original_type)
typedef LB_ABI_INFO(lbAbiInfoType);
@@ -1674,6 +1700,13 @@ namespace lbAbiArm64 {
}
GB_ASSERT(size <= 16);
if (LLVMGetTypeKind(return_type) == LLVMVectorTypeKind) {
// A vector too narrow to be a short vector is still RETURNED as
// itself. clang coerces a 4-byte vector argument to `i32` and puts
// it in w0, but returns `<4 x i8>` in v0; coercing the return too
// picks the wrong register file.
return lb_arg_type_direct(return_type, nullptr, nullptr, nullptr);
}
LLVMTypeRef cast_type = nullptr;
if (size == 0) {
cast_type = LLVMStructTypeInContext(c, nullptr, 0, false);

View File

@@ -2655,7 +2655,17 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
// 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));
LLVMTypeRef field_llvm_type = lb_type(m, field_type);
// `max_simd_align` can cap a member below what LLVM gives the lowered
// type. Unpacked, LLVM lays the struct out by its own alignment and the
// member moves: `struct{i8, #simd[8]f32}` is 48 bytes here and 64 to
// LLVM on every target that caps the vector at 16.
i64 natural_align = lb_llvm_natural_alignof(field_llvm_type);
requires_packing = requires_packing || ((offset % natural_align) != 0) ||
natural_align > full_type_align;
array_add(&fields, field_llvm_type);
prev_offset = offset + type_size_of(field->type);
}