mirror of
https://github.com/odin-lang/Odin.git
synced 2026-08-23 21:41:34 +00:00
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:
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user