authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2024-04-06 03:45:23-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-04-06 13:02:55-07:00
logf668c8bfd65489d1d38716e2973e0ee1cf0e8c52
treee727d4ffb756663eebe3844eb362b995e838e2be
parent34bb670bb695969ce57cb3fc0e1bb673a1cc7243

x86_64: fix abi of nested structs


3 files changed, 225 insertions(+), 105 deletions(-)

src/arch/x86_64/abi.zig+102-99
...@@ -13,7 +13,7 @@ pub const Class = enum {...@@ -13,7 +13,7 @@ pub const Class = enum {
13 integer_per_element,13 integer_per_element,
14};14};
1515
16pub fn classifyWindows(ty: Type, mod: *Module) Class {16pub fn classifyWindows(ty: Type, zcu: *Zcu) Class {
17 // https://docs.microsoft.com/en-gb/cpp/build/x64-calling-convention?view=vs-201717 // https://docs.microsoft.com/en-gb/cpp/build/x64-calling-convention?view=vs-2017
18 // "There's a strict one-to-one correspondence between a function call's arguments18 // "There's a strict one-to-one correspondence between a function call's arguments
19 // and the registers used for those arguments. Any argument that doesn't fit in 819 // and the registers used for those arguments. Any argument that doesn't fit in 8
...@@ -22,7 +22,7 @@ pub fn classifyWindows(ty: Type, mod: *Module) Class {...@@ -22,7 +22,7 @@ pub fn classifyWindows(ty: Type, mod: *Module) Class {
22 // "All floating point operations are done using the 16 XMM registers."22 // "All floating point operations are done using the 16 XMM registers."
23 // "Structs and unions of size 8, 16, 32, or 64 bits, and __m64 types, are passed23 // "Structs and unions of size 8, 16, 32, or 64 bits, and __m64 types, are passed
24 // as if they were integers of the same size."24 // as if they were integers of the same size."
25 switch (ty.zigTypeTag(mod)) {25 switch (ty.zigTypeTag(zcu)) {
26 .Pointer,26 .Pointer,
27 .Int,27 .Int,
28 .Bool,28 .Bool,
...@@ -37,12 +37,12 @@ pub fn classifyWindows(ty: Type, mod: *Module) Class {...@@ -37,12 +37,12 @@ pub fn classifyWindows(ty: Type, mod: *Module) Class {
37 .ErrorUnion,37 .ErrorUnion,
38 .AnyFrame,38 .AnyFrame,
39 .Frame,39 .Frame,
40 => switch (ty.abiSize(mod)) {40 => switch (ty.abiSize(zcu)) {
41 0 => unreachable,41 0 => unreachable,
42 1, 2, 4, 8 => return .integer,42 1, 2, 4, 8 => return .integer,
43 else => switch (ty.zigTypeTag(mod)) {43 else => switch (ty.zigTypeTag(zcu)) {
44 .Int => return .win_i128,44 .Int => return .win_i128,
45 .Struct, .Union => if (ty.containerLayout(mod) == .@"packed") {45 .Struct, .Union => if (ty.containerLayout(zcu) == .@"packed") {
46 return .win_i128;46 return .win_i128;
47 } else {47 } else {
48 return .memory;48 return .memory;
...@@ -69,16 +69,16 @@ pub const Context = enum { ret, arg, field, other };...@@ -69,16 +69,16 @@ pub const Context = enum { ret, arg, field, other };
6969
70/// There are a maximum of 8 possible return slots. Returned values are in70/// There are a maximum of 8 possible return slots. Returned values are in
71/// the beginning of the array; unused slots are filled with .none.71/// the beginning of the array; unused slots are filled with .none.
72pub fn classifySystemV(ty: Type, mod: *Module, ctx: Context) [8]Class {72pub fn classifySystemV(ty: Type, zcu: *Zcu, ctx: Context) [8]Class {
73 const ip = &mod.intern_pool;73 const ip = &zcu.intern_pool;
74 const target = mod.getTarget();74 const target = zcu.getTarget();
75 const memory_class = [_]Class{75 const memory_class = [_]Class{
76 .memory, .none, .none, .none,76 .memory, .none, .none, .none,
77 .none, .none, .none, .none,77 .none, .none, .none, .none,
78 };78 };
79 var result = [1]Class{.none} ** 8;79 var result = [1]Class{.none} ** 8;
80 switch (ty.zigTypeTag(mod)) {80 switch (ty.zigTypeTag(zcu)) {
81 .Pointer => switch (ty.ptrSize(mod)) {81 .Pointer => switch (ty.ptrSize(zcu)) {
82 .Slice => {82 .Slice => {
83 result[0] = .integer;83 result[0] = .integer;
84 result[1] = .integer;84 result[1] = .integer;
...@@ -90,7 +90,7 @@ pub fn classifySystemV(ty: Type, mod: *Module, ctx: Context) [8]Class {...@@ -90,7 +90,7 @@ pub fn classifySystemV(ty: Type, mod: *Module, ctx: Context) [8]Class {
90 },90 },
91 },91 },
92 .Int, .Enum, .ErrorSet => {92 .Int, .Enum, .ErrorSet => {
93 const bits = ty.intInfo(mod).bits;93 const bits = ty.intInfo(zcu).bits;
94 if (bits <= 64) {94 if (bits <= 64) {
95 result[0] = .integer;95 result[0] = .integer;
96 return result;96 return result;
...@@ -160,8 +160,8 @@ pub fn classifySystemV(ty: Type, mod: *Module, ctx: Context) [8]Class {...@@ -160,8 +160,8 @@ pub fn classifySystemV(ty: Type, mod: *Module, ctx: Context) [8]Class {
160 else => unreachable,160 else => unreachable,
161 },161 },
162 .Vector => {162 .Vector => {
163 const elem_ty = ty.childType(mod);163 const elem_ty = ty.childType(zcu);
164 const bits = elem_ty.bitSize(mod) * ty.arrayLen(mod);164 const bits = elem_ty.bitSize(zcu) * ty.arrayLen(zcu);
165 if (elem_ty.toIntern() == .bool_type) {165 if (elem_ty.toIntern() == .bool_type) {
166 if (bits <= 32) return .{166 if (bits <= 32) return .{
167 .integer, .none, .none, .none,167 .integer, .none, .none, .none,
...@@ -225,7 +225,7 @@ pub fn classifySystemV(ty: Type, mod: *Module, ctx: Context) [8]Class {...@@ -225,7 +225,7 @@ pub fn classifySystemV(ty: Type, mod: *Module, ctx: Context) [8]Class {
225 return memory_class;225 return memory_class;
226 },226 },
227 .Optional => {227 .Optional => {
228 if (ty.isPtrLikeOptional(mod)) {228 if (ty.isPtrLikeOptional(zcu)) {
229 result[0] = .integer;229 result[0] = .integer;
230 return result;230 return result;
231 }231 }
...@@ -236,9 +236,9 @@ pub fn classifySystemV(ty: Type, mod: *Module, ctx: Context) [8]Class {...@@ -236,9 +236,9 @@ pub fn classifySystemV(ty: Type, mod: *Module, ctx: Context) [8]Class {
236 // it contains unaligned fields, it has class MEMORY"236 // it contains unaligned fields, it has class MEMORY"
237 // "If the size of the aggregate exceeds a single eightbyte, each is classified237 // "If the size of the aggregate exceeds a single eightbyte, each is classified
238 // separately.".238 // separately.".
239 const struct_type = mod.typeToStruct(ty).?;239 const loaded_struct = ip.loadStructType(ty.toIntern());
240 const ty_size = ty.abiSize(mod);240 const ty_size = ty.abiSize(zcu);
241 if (struct_type.layout == .@"packed") {241 if (loaded_struct.layout == .@"packed") {
242 assert(ty_size <= 16);242 assert(ty_size <= 16);
243 result[0] = .integer;243 result[0] = .integer;
244 if (ty_size > 8) result[1] = .integer;244 if (ty_size > 8) result[1] = .integer;
...@@ -247,82 +247,8 @@ pub fn classifySystemV(ty: Type, mod: *Module, ctx: Context) [8]Class {...@@ -247,82 +247,8 @@ pub fn classifySystemV(ty: Type, mod: *Module, ctx: Context) [8]Class {
247 if (ty_size > 64)247 if (ty_size > 64)
248 return memory_class;248 return memory_class;
249249
250 var result_i: usize = 0; // out of 8250 var byte_offset: u64 = 0;
251 var byte_i: usize = 0; // out of 8251 classifySystemVStruct(&result, &byte_offset, loaded_struct, zcu);
252 for (struct_type.field_types.get(ip), 0..) |field_ty_ip, i| {
253 const field_ty = Type.fromInterned(field_ty_ip);
254 const field_align = struct_type.fieldAlign(ip, i);
255 if (field_align != .none and field_align.compare(.lt, field_ty.abiAlignment(mod)))
256 return memory_class;
257 const field_size = field_ty.abiSize(mod);
258 const field_class_array = classifySystemV(field_ty, mod, .field);
259 const field_class = std.mem.sliceTo(&field_class_array, .none);
260 if (byte_i + field_size <= 8) {
261 // Combine this field with the previous one.
262 combine: {
263 // "If both classes are equal, this is the resulting class."
264 if (result[result_i] == field_class[0]) {
265 if (result[result_i] == .float) {
266 result[result_i] = .float_combine;
267 }
268 break :combine;
269 }
270
271 // "If one of the classes is NO_CLASS, the resulting class
272 // is the other class."
273 if (result[result_i] == .none) {
274 result[result_i] = field_class[0];
275 break :combine;
276 }
277 assert(field_class[0] != .none);
278
279 // "If one of the classes is MEMORY, the result is the MEMORY class."
280 if (result[result_i] == .memory or field_class[0] == .memory) {
281 result[result_i] = .memory;
282 break :combine;
283 }
284
285 // "If one of the classes is INTEGER, the result is the INTEGER."
286 if (result[result_i] == .integer or field_class[0] == .integer) {
287 result[result_i] = .integer;
288 break :combine;
289 }
290
291 // "If one of the classes is X87, X87UP, COMPLEX_X87 class,
292 // MEMORY is used as class."
293 if (result[result_i] == .x87 or
294 result[result_i] == .x87up or
295 result[result_i] == .complex_x87 or
296 field_class[0] == .x87 or
297 field_class[0] == .x87up or
298 field_class[0] == .complex_x87)
299 {
300 result[result_i] = .memory;
301 break :combine;
302 }
303
304 // "Otherwise class SSE is used."
305 result[result_i] = .sse;
306 }
307 byte_i += @as(usize, @intCast(field_size));
308 if (byte_i == 8) {
309 byte_i = 0;
310 result_i += 1;
311 }
312 } else {
313 // Cannot combine this field with the previous one.
314 if (byte_i != 0) {
315 byte_i = 0;
316 result_i += 1;
317 }
318 @memcpy(result[result_i..][0..field_class.len], field_class);
319 result_i += field_class.len;
320 // If there are any bytes leftover, we have to try to combine
321 // the next field with them.
322 byte_i = @as(usize, @intCast(field_size % 8));
323 if (byte_i != 0) result_i -= 1;
324 }
325 }
326252
327 // Post-merger cleanup253 // Post-merger cleanup
328254
...@@ -354,8 +280,8 @@ pub fn classifySystemV(ty: Type, mod: *Module, ctx: Context) [8]Class {...@@ -354,8 +280,8 @@ pub fn classifySystemV(ty: Type, mod: *Module, ctx: Context) [8]Class {
354 // it contains unaligned fields, it has class MEMORY"280 // it contains unaligned fields, it has class MEMORY"
355 // "If the size of the aggregate exceeds a single eightbyte, each is classified281 // "If the size of the aggregate exceeds a single eightbyte, each is classified
356 // separately.".282 // separately.".
357 const union_obj = mod.typeToUnion(ty).?;283 const union_obj = zcu.typeToUnion(ty).?;
358 const ty_size = mod.unionAbiSize(union_obj);284 const ty_size = zcu.unionAbiSize(union_obj);
359 if (union_obj.getLayout(ip) == .@"packed") {285 if (union_obj.getLayout(ip) == .@"packed") {
360 assert(ty_size <= 16);286 assert(ty_size <= 16);
361 result[0] = .integer;287 result[0] = .integer;
...@@ -368,12 +294,12 @@ pub fn classifySystemV(ty: Type, mod: *Module, ctx: Context) [8]Class {...@@ -368,12 +294,12 @@ pub fn classifySystemV(ty: Type, mod: *Module, ctx: Context) [8]Class {
368 for (union_obj.field_types.get(ip), 0..) |field_ty, field_index| {294 for (union_obj.field_types.get(ip), 0..) |field_ty, field_index| {
369 const field_align = union_obj.fieldAlign(ip, @intCast(field_index));295 const field_align = union_obj.fieldAlign(ip, @intCast(field_index));
370 if (field_align != .none and296 if (field_align != .none and
371 field_align.compare(.lt, Type.fromInterned(field_ty).abiAlignment(mod)))297 field_align.compare(.lt, Type.fromInterned(field_ty).abiAlignment(zcu)))
372 {298 {
373 return memory_class;299 return memory_class;
374 }300 }
375 // Combine this field with the previous one.301 // Combine this field with the previous one.
376 const field_class = classifySystemV(Type.fromInterned(field_ty), mod, .field);302 const field_class = classifySystemV(Type.fromInterned(field_ty), zcu, .field);
377 for (&result, 0..) |*result_item, i| {303 for (&result, 0..) |*result_item, i| {
378 const field_item = field_class[i];304 const field_item = field_class[i];
379 // "If both classes are equal, this is the resulting class."305 // "If both classes are equal, this is the resulting class."
...@@ -447,7 +373,7 @@ pub fn classifySystemV(ty: Type, mod: *Module, ctx: Context) [8]Class {...@@ -447,7 +373,7 @@ pub fn classifySystemV(ty: Type, mod: *Module, ctx: Context) [8]Class {
447 return result;373 return result;
448 },374 },
449 .Array => {375 .Array => {
450 const ty_size = ty.abiSize(mod);376 const ty_size = ty.abiSize(zcu);
451 if (ty_size <= 8) {377 if (ty_size <= 8) {
452 result[0] = .integer;378 result[0] = .integer;
453 return result;379 return result;
...@@ -463,6 +389,82 @@ pub fn classifySystemV(ty: Type, mod: *Module, ctx: Context) [8]Class {...@@ -463,6 +389,82 @@ pub fn classifySystemV(ty: Type, mod: *Module, ctx: Context) [8]Class {
463 }389 }
464}390}
465391
392fn classifySystemVStruct(
393 result: *[8]Class,
394 byte_offset: *u64,
395 loaded_struct: InternPool.LoadedStructType,
396 zcu: *Zcu,
397) void {
398 const ip = &zcu.intern_pool;
399 var field_it = loaded_struct.iterateRuntimeOrder(ip);
400 while (field_it.next()) |field_index| {
401 const field_ty = Type.fromInterned(loaded_struct.field_types.get(ip)[field_index]);
402 const field_align = loaded_struct.fieldAlign(ip, field_index);
403 byte_offset.* = std.mem.alignForward(
404 u64,
405 byte_offset.*,
406 field_align.toByteUnits() orelse field_ty.abiAlignment(zcu).toByteUnits().?,
407 );
408 if (zcu.typeToStruct(field_ty)) |field_loaded_struct| {
409 if (field_loaded_struct.layout != .@"packed") {
410 classifySystemVStruct(result, byte_offset, field_loaded_struct, zcu);
411 continue;
412 }
413 }
414 const field_class = std.mem.sliceTo(&classifySystemV(field_ty, zcu, .field), .none);
415 const field_size = field_ty.abiSize(zcu);
416 combine: {
417 // Combine this field with the previous one.
418 const result_class = &result[@intCast(byte_offset.* / 8)];
419 // "If both classes are equal, this is the resulting class."
420 if (result_class.* == field_class[0]) {
421 if (result_class.* == .float) {
422 result_class.* = .float_combine;
423 }
424 break :combine;
425 }
426
427 // "If one of the classes is NO_CLASS, the resulting class
428 // is the other class."
429 if (result_class.* == .none) {
430 result_class.* = field_class[0];
431 break :combine;
432 }
433 assert(field_class[0] != .none);
434
435 // "If one of the classes is MEMORY, the result is the MEMORY class."
436 if (result_class.* == .memory or field_class[0] == .memory) {
437 result_class.* = .memory;
438 break :combine;
439 }
440
441 // "If one of the classes is INTEGER, the result is the INTEGER."
442 if (result_class.* == .integer or field_class[0] == .integer) {
443 result_class.* = .integer;
444 break :combine;
445 }
446
447 // "If one of the classes is X87, X87UP, COMPLEX_X87 class,
448 // MEMORY is used as class."
449 if (result_class.* == .x87 or
450 result_class.* == .x87up or
451 result_class.* == .complex_x87 or
452 field_class[0] == .x87 or
453 field_class[0] == .x87up or
454 field_class[0] == .complex_x87)
455 {
456 result_class.* = .memory;
457 break :combine;
458 }
459
460 // "Otherwise class SSE is used."
461 result_class.* = .sse;
462 }
463 @memcpy(result[@intCast(byte_offset.* / 8 + 1)..][0 .. field_class.len - 1], field_class[1..]);
464 byte_offset.* += field_size;
465 }
466}
467
466pub const SysV = struct {468pub const SysV = struct {
467 /// Note that .rsp and .rbp also belong to this set, however, we never expect to use them469 /// Note that .rsp and .rbp also belong to this set, however, we never expect to use them
468 /// for anything else but stack offset tracking therefore we exclude them from this set.470 /// for anything else but stack offset tracking therefore we exclude them from this set.
...@@ -592,8 +594,9 @@ const std = @import("std");...@@ -592,8 +594,9 @@ const std = @import("std");
592const assert = std.debug.assert;594const assert = std.debug.assert;
593const testing = std.testing;595const testing = std.testing;
594596
595const Module = @import("../../Module.zig");597const InternPool = @import("../../InternPool.zig");
596const Register = @import("bits.zig").Register;598const Register = @import("bits.zig").Register;
597const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;599const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
598const Type = @import("../../type.zig").Type;600const Type = @import("../../type.zig").Type;
599const Value = @import("../../Value.zig");601const Value = @import("../../Value.zig");
602const Zcu = @import("../../Module.zig");
test/c_abi/cfuncs.c+59-1
...@@ -227,6 +227,48 @@ void c_struct_u64_u64_8(size_t, size_t, size_t, size_t, size_t, size_t, size_t,...@@ -227,6 +227,48 @@ void c_struct_u64_u64_8(size_t, size_t, size_t, size_t, size_t, size_t, size_t,
227 assert_or_panic(s.b == 40);227 assert_or_panic(s.b == 40);
228}228}
229229
230struct Struct_f32f32_f32 {
231 struct {
232 float b, c;
233 } a;
234 float d;
235};
236
237struct Struct_f32f32_f32 zig_ret_struct_f32f32_f32(void);
238
239void zig_struct_f32f32_f32(struct Struct_f32f32_f32);
240
241struct Struct_f32f32_f32 c_ret_struct_f32f32_f32(void) {
242 return (struct Struct_f32f32_f32){ { 1.0f, 2.0f }, 3.0f };
243}
244
245void c_struct_f32f32_f32(struct Struct_f32f32_f32 s) {
246 assert_or_panic(s.a.b == 1.0f);
247 assert_or_panic(s.a.c == 2.0f);
248 assert_or_panic(s.d == 3.0f);
249}
250
251struct Struct_f32_f32f32 {
252 float a;
253 struct {
254 float c, d;
255 } b;
256};
257
258struct Struct_f32_f32f32 zig_ret_struct_f32_f32f32(void);
259
260void zig_struct_f32_f32f32(struct Struct_f32_f32f32);
261
262struct Struct_f32_f32f32 c_ret_struct_f32_f32f32(void) {
263 return (struct Struct_f32_f32f32){ 1.0f, { 2.0f, 3.0f } };
264}
265
266void c_struct_f32_f32f32(struct Struct_f32_f32f32 s) {
267 assert_or_panic(s.a == 1.0f);
268 assert_or_panic(s.b.c == 2.0f);
269 assert_or_panic(s.b.d == 3.0f);
270}
271
230struct BigStruct {272struct BigStruct {
231 uint64_t a;273 uint64_t a;
232 uint64_t b;274 uint64_t b;
...@@ -2603,9 +2645,25 @@ void run_c_tests(void) {...@@ -2603,9 +2645,25 @@ void run_c_tests(void) {
2603 zig_struct_u64_u64_7(0, 1, 2, 3, 4, 5, 6, (struct Struct_u64_u64){ .a = 17, .b = 18 });2645 zig_struct_u64_u64_7(0, 1, 2, 3, 4, 5, 6, (struct Struct_u64_u64){ .a = 17, .b = 18 });
2604 zig_struct_u64_u64_8(0, 1, 2, 3, 4, 5, 6, 7, (struct Struct_u64_u64){ .a = 19, .b = 20 });2646 zig_struct_u64_u64_8(0, 1, 2, 3, 4, 5, 6, 7, (struct Struct_u64_u64){ .a = 19, .b = 20 });
2605 }2647 }
2648
2649#if !defined(ZIG_RISCV64)
2650 {
2651 struct Struct_f32f32_f32 s = zig_ret_struct_f32f32_f32();
2652 assert_or_panic(s.a.b == 1.0f);
2653 assert_or_panic(s.a.c == 2.0f);
2654 assert_or_panic(s.d == 3.0f);
2655 zig_struct_f32f32_f32((struct Struct_f32f32_f32){ { 1.0f, 2.0f }, 3.0f });
2656 }
2657
2658 {
2659 struct Struct_f32_f32f32 s = zig_ret_struct_f32_f32f32();
2660 assert_or_panic(s.a == 1.0f);
2661 assert_or_panic(s.b.c == 2.0f);
2662 assert_or_panic(s.b.d == 3.0f);
2663 zig_struct_f32_f32f32((struct Struct_f32_f32f32){ 1.0f, { 2.0f, 3.0f } });
2664 }
2606#endif2665#endif
26072666
2608#if !defined __mips__ && !defined ZIG_PPC32
2609 {2667 {
2610 struct BigStruct s = {1, 2, 3, 4, 5};2668 struct BigStruct s = {1, 2, 3, 4, 5};
2611 zig_big_struct(s);2669 zig_big_struct(s);
test/c_abi/main.zig+64-5
...@@ -273,6 +273,7 @@ const Struct_u64_u64 = extern struct {...@@ -273,6 +273,7 @@ const Struct_u64_u64 = extern struct {
273export fn zig_ret_struct_u64_u64() Struct_u64_u64 {273export fn zig_ret_struct_u64_u64() Struct_u64_u64 {
274 return .{ .a = 1, .b = 2 };274 return .{ .a = 1, .b = 2 };
275}275}
276
276export fn zig_struct_u64_u64_0(s: Struct_u64_u64) void {277export fn zig_struct_u64_u64_0(s: Struct_u64_u64) void {
277 expect(s.a == 3) catch @panic("test failure");278 expect(s.a == 3) catch @panic("test failure");
278 expect(s.b == 4) catch @panic("test failure");279 expect(s.b == 4) catch @panic("test failure");
...@@ -326,11 +327,9 @@ test "C ABI struct u64 u64" {...@@ -326,11 +327,9 @@ test "C ABI struct u64 u64" {
326 if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;327 if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
327 if (builtin.cpu.arch.isPPC()) return error.SkipZigTest;328 if (builtin.cpu.arch.isPPC()) return error.SkipZigTest;
328329
329 {330 const s = c_ret_struct_u64_u64();
330 const s = c_ret_struct_u64_u64();331 try expect(s.a == 21);
331 try expect(s.a == 21);332 try expect(s.b == 22);
332 try expect(s.b == 22);
333 }
334 c_struct_u64_u64_0(.{ .a = 23, .b = 24 });333 c_struct_u64_u64_0(.{ .a = 23, .b = 24 });
335 c_struct_u64_u64_1(0, .{ .a = 25, .b = 26 });334 c_struct_u64_u64_1(0, .{ .a = 25, .b = 26 });
336 c_struct_u64_u64_2(0, 1, .{ .a = 27, .b = 28 });335 c_struct_u64_u64_2(0, 1, .{ .a = 27, .b = 28 });
...@@ -342,6 +341,66 @@ test "C ABI struct u64 u64" {...@@ -342,6 +341,66 @@ test "C ABI struct u64 u64" {
342 c_struct_u64_u64_8(0, 1, 2, 3, 4, 5, 6, 7, .{ .a = 39, .b = 40 });341 c_struct_u64_u64_8(0, 1, 2, 3, 4, 5, 6, 7, .{ .a = 39, .b = 40 });
343}342}
344343
344const Struct_f32f32_f32 = extern struct {
345 a: extern struct { b: f32, c: f32 },
346 d: f32,
347};
348
349export fn zig_ret_struct_f32f32_f32() Struct_f32f32_f32 {
350 return .{ .a = .{ .b = 1.0, .c = 2.0 }, .d = 3.0 };
351}
352
353export fn zig_struct_f32f32_f32(s: Struct_f32f32_f32) void {
354 expect(s.a.b == 1.0) catch @panic("test failure");
355 expect(s.a.c == 2.0) catch @panic("test failure");
356 expect(s.d == 3.0) catch @panic("test failure");
357}
358
359extern fn c_ret_struct_f32f32_f32() Struct_f32f32_f32;
360
361extern fn c_struct_f32f32_f32(Struct_f32f32_f32) void;
362
363test "C ABI struct {f32,f32} f32" {
364 if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
365 if (builtin.cpu.arch.isPPC()) return error.SkipZigTest;
366
367 const s = c_ret_struct_f32f32_f32();
368 try expect(s.a.b == 1.0);
369 try expect(s.a.c == 2.0);
370 try expect(s.d == 3.0);
371 c_struct_f32f32_f32(.{ .a = .{ .b = 1.0, .c = 2.0 }, .d = 3.0 });
372}
373
374const Struct_f32_f32f32 = extern struct {
375 a: f32,
376 b: extern struct { c: f32, d: f32 },
377};
378
379export fn zig_ret_struct_f32_f32f32() Struct_f32_f32f32 {
380 return .{ .a = 1.0, .b = .{ .c = 2.0, .d = 3.0 } };
381}
382
383export fn zig_struct_f32_f32f32(s: Struct_f32_f32f32) void {
384 expect(s.a == 1.0) catch @panic("test failure");
385 expect(s.b.c == 2.0) catch @panic("test failure");
386 expect(s.b.d == 3.0) catch @panic("test failure");
387}
388
389extern fn c_ret_struct_f32_f32f32() Struct_f32_f32f32;
390
391extern fn c_struct_f32_f32f32(Struct_f32_f32f32) void;
392
393test "C ABI struct f32 {f32,f32}" {
394 if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
395 if (builtin.cpu.arch.isPPC()) return error.SkipZigTest;
396
397 const s = c_ret_struct_f32_f32f32();
398 try expect(s.a == 1.0);
399 try expect(s.b.c == 2.0);
400 try expect(s.b.d == 3.0);
401 c_struct_f32_f32f32(.{ .a = 1.0, .b = .{ .c = 2.0, .d = 3.0 } });
402}
403
345const BigStruct = extern struct {404const BigStruct = extern struct {
346 a: u64,405 a: u64,
347 b: u64,406 b: u64,