diff --git a/src/codegen/wasm/abi.zig b/src/codegen/wasm/abi.zig index 5c88caddb98a35c6f192c647fb5d53eb032375a5..1952c65227e8d57b0c164a62286814265bae0e3c 100644 --- a/src/codegen/wasm/abi.zig +++ b/src/codegen/wasm/abi.zig @@ -71,27 +71,35 @@ pub fn classifyTypeForLlvm(ty: Type, zcu: *const Zcu) LlvmClass { }, .@"struct" => { const struct_type = zcu.typeToStruct(ty).?; - if (struct_type.layout == .@"packed") { - return .{ .direct = ty }; + switch (struct_type.layout) { + .auto => unreachable, + .@"packed" => return .{ .direct = ty }, + .@"extern" => {}, } - if (struct_type.field_types.len > 1) { - // The struct type is non-scalar. - return .indirect; - } - const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[0]); - const explicit_align = struct_type.field_aligns.getOrNone(ip, 0); - if (explicit_align != .none) { - if (explicit_align.compareStrict(.gt, field_ty.abiAlignment(zcu))) + var opt_single_field_ty: ?Type = null; + for (struct_type.field_types.get(ip), 0..) |field_ty_index, field_index| { + const field_ty: Type = .fromInterned(field_ty_index); + if (!field_ty.hasRuntimeBits(zcu)) continue; + + if (opt_single_field_ty != null) { + return .indirect; + } + + const field_align = struct_type.field_aligns.getOrNone(ip, field_index); + if (field_align != .none and field_align.compareStrict(.gt, field_ty.abiAlignment(zcu))) { return .indirect; + } + opt_single_field_ty = field_ty; } - if (field_ty.zigTypeTag(zcu) == .array) { - switch (field_ty.arrayLenIncludingSentinel(zcu)) { + const single_field_ty = opt_single_field_ty.?; + if (single_field_ty.zigTypeTag(zcu) == .array) { + switch (single_field_ty.arrayLenIncludingSentinel(zcu)) { 0 => unreachable, - 1 => return classifyTypeForLlvm(field_ty.childType(zcu), zcu), + 1 => return classifyTypeForLlvm(single_field_ty.childType(zcu), zcu), else => {}, } } - return classifyTypeForLlvm(field_ty, zcu); + return classifyTypeForLlvm(single_field_ty, zcu); }, .@"union" => { const union_obj = zcu.typeToUnion(ty).?; diff --git a/test/c_abi/cfuncs.c b/test/c_abi/cfuncs.c index 110e5fd18dd4a5593c7004f439e85eed82c46a1d..327bc04ceb34148541b994bb894f04bea85bf210 100644 --- a/test/c_abi/cfuncs.c +++ b/test/c_abi/cfuncs.c @@ -15194,6 +15194,22 @@ void c_test_struct_f32_f32_f32_f32_f32(void) { zig_struct_f32_f32_f32_f32_f32((struct Struct_f32_f32_f32_f32_f32){ .a = 6, .b = 7, .c = 8, .d = 9, .e = 10 }, 11); } +struct Struct_f32 zig_ret_struct_void_f32(void); +void zig_struct_void_f32(struct Struct_f32, size_t); + +struct Struct_f32 c_ret_struct_void_f32(void) { + return (struct Struct_f32){ .a = 4 }; +} +void c_struct_void_f32(struct Struct_f32 s, size_t i) { + assert_or_panic(s.a == 5); + assert_or_panic(i == 6); +} +void c_test_struct_void_f32(void) { + struct Struct_f32 s = zig_ret_struct_void_f32(); + assert_or_panic(s.a == 1); + zig_struct_void_f32((struct Struct_f32){ .a = 2 }, 3); +} + struct Struct_array_1_f32 { float a[1]; }; diff --git a/test/c_abi/main.zig b/test/c_abi/main.zig index 2286d838b86402f9e5e03c30e7db3732c2e3c3f4..53e3890f31fa28014d84fc96012d21dbda3699dd 100644 --- a/test/c_abi/main.zig +++ b/test/c_abi/main.zig @@ -16103,6 +16103,34 @@ test "struct f32, f32, f32, f32, f32" { c_test_struct_f32_f32_f32_f32_f32(); } +const Struct_void_f32 = extern struct { + _: void = {}, + a: f32, +}; + +export fn zig_ret_struct_void_f32() Struct_void_f32 { + return .{ .a = 1 }; +} +export fn zig_struct_void_f32(s: Struct_void_f32, i: usize) void { + expect(s.a == 2) catch @panic("test failure"); + expect(i == 3) catch @panic("test failure"); +} + +extern fn c_ret_struct_void_f32() Struct_void_f32; +extern fn c_struct_void_f32(Struct_void_f32, usize) void; +extern fn c_test_struct_void_f32() void; + +test "struct void, f32" { + if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest; + if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest; + if (builtin.cpu.arch == .x86 and builtin.os.tag == .windows) return error.SkipZigTest; + + const s = c_ret_struct_void_f32(); + try expect(s.a == 4); + c_struct_void_f32(.{ .a = 5 }, 6); + c_test_struct_void_f32(); +} + const Struct_array_1_f32 = extern struct { a: [1]f32, };