diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index da72ecd67..37df13b2c 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -726,13 +726,19 @@ namespace lbAbiAmd64SysV { gb_internal bool is_aggregate(LLVMTypeRef type) { // A single-member wrapper is passed like its member, but only while that // member still fits one eightbyte. `struct{i128}` needs two registers and - // goes to memory when they are gone, where a bare `i128` does not -- clang - // emits `byval align 16` for the struct and a plain `i128` for the scalar. + // goes to memory when they are gone. A bare `i128` does not; clang emits + // `byval align 16` for the struct and a plain `i128` for the scalar. LLVMTypeKind kind = LLVMGetTypeKind(type); switch (kind) { case LLVMStructTypeKind: if (LLVMCountStructElementTypes(type) == 1) { LLVMTypeRef elem = LLVMStructGetTypeAtIndex(type, 0); + // A VECTOR member keeps the wrapper an aggregate whatever its size: + // LLVM gives a vector its own oversized stack slot, so an unwrapped + // `struct{#simd[2]f32}` takes 16 bytes where the ABI wants 8. + if (LLVMGetTypeKind(elem) == LLVMVectorTypeKind) { + return true; + } return lb_sizeof(elem) > 8 || is_aggregate(elem); } return true; diff --git a/tests/abi/gen.py b/tests/abi/gen.py index bf2d8b50b..4a9c86163 100644 --- a/tests/abi/gen.py +++ b/tests/abi/gen.py @@ -372,10 +372,13 @@ def build(): add("zarr", "struct { z: [0]f32 }", "struct { float z[0]; }", [], tier=TIER_GNU) # --- an array OF vectors, and a vector wider than one register + # The C paths index the vector array directly; only the ODIN side needs the + # hatch. add("av2_f32", "struct { a: [2]#simd[4]f32 }", "struct { rx_v4f a[2]; }", - [], tier=TIER_GNU, + [leaf2("", f"a[{i // 4}][{i % 4}]", "f32", f"{i + 1}.5") for i in range(8)], + tier=TIER_GNU, odin_set=["{}.a[0] = {1.5, 2.5, 3.5, 4.5}", "{}.a[1] = {5.5, 6.5, 7.5, 8.5}"], odin_get=[("simd.extract({}.a[0], 0)", "f32(1.5)"), ("simd.extract({}.a[1], 3)", "f32(8.5)")])