authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-07-28 12:48:01-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-07-28 19:27:08-04:00
log125b453c58b6d94a628c94d46e5038e06183c54c
tree1e07b37d54cb62761003c937d8a937fdf90b7186
parentc80609dfecb57a3830583eeeb3e0fce5860d657b

llvm: fix SysV C abi for structs smaller than two eightbytes

Closes #16038 Closes #16288

7 files changed, 126 insertions(+), 15 deletions(-)

src/codegen/llvm.zig+32-9
...@@ -5421,10 +5421,13 @@ pub const FuncGen = struct {...@@ -5421,10 +5421,13 @@ pub const FuncGen = struct {
5421 // In this case the function return type is honoring the calling convention by having5421 // In this case the function return type is honoring the calling convention by having
5422 // a different LLVM type than the usual one. We solve this here at the callsite5422 // a different LLVM type than the usual one. We solve this here at the callsite
5423 // by using our canonical type, then loading it if necessary.5423 // by using our canonical type, then loading it if necessary.
5424 const alignment = Builder.Alignment.fromByteUnits(5424 const alignment = Builder.Alignment.fromByteUnits(@max(
5425 o.target_data.abiAlignmentOfType(abi_ret_ty.toLlvm(&o.builder)),5425 o.target_data.abiAlignmentOfType(abi_ret_ty.toLlvm(&o.builder)),
5426 );5426 return_type.abiAlignment(mod),
5427 const rp = try self.buildAlloca(llvm_ret_ty, alignment);5427 ));
5428 assert(o.target_data.abiSizeOfType(abi_ret_ty.toLlvm(&o.builder)) >=
5429 o.target_data.abiSizeOfType(llvm_ret_ty.toLlvm(&o.builder)));
5430 const rp = try self.buildAlloca(abi_ret_ty, alignment);
5428 _ = try self.wip.store(.normal, call, rp, alignment);5431 _ = try self.wip.store(.normal, call, rp, alignment);
5429 return if (isByRef(return_type, mod))5432 return if (isByRef(return_type, mod))
5430 rp5433 rp
...@@ -11555,8 +11558,17 @@ fn lowerSystemVFnRetTy(o: *Object, fn_info: InternPool.Key.FuncType) Allocator.E...@@ -11555,8 +11558,17 @@ fn lowerSystemVFnRetTy(o: *Object, fn_info: InternPool.Key.FuncType) Allocator.E
11555 .none => break,11558 .none => break,
11556 }11559 }
11557 }11560 }
11558 if (classes[0] == .integer and classes[1] == .none) {11561 const first_non_integer = std.mem.indexOfNone(x86_64_abi.Class, &classes, &.{.integer});
11559 return o.builder.intType(@intCast(return_type.abiSize(mod) * 8));11562 if (first_non_integer == null or classes[first_non_integer.?] == .none) {
11563 assert(first_non_integer orelse classes.len == types_index);
11564 if (mod.intern_pool.indexToKey(return_type.toIntern()) == .struct_type) {
11565 var struct_it = return_type.iterateStructOffsets(mod);
11566 while (struct_it.next()) |_| {}
11567 assert((std.math.divCeil(u64, struct_it.offset, 8) catch unreachable) == types_index);
11568 if (struct_it.offset % 8 > 0) types_buffer[types_index - 1] =
11569 try o.builder.intType(@intCast(struct_it.offset % 8 * 8));
11570 }
11571 if (types_index == 1) return types_buffer[0];
11560 }11572 }
11561 return o.builder.structType(.normal, types_buffer[0..types_index]);11573 return o.builder.structType(.normal, types_buffer[0..types_index]);
11562}11574}
...@@ -11807,10 +11819,21 @@ const ParamTypeIterator = struct {...@@ -11807,10 +11819,21 @@ const ParamTypeIterator = struct {
11807 .none => break,11819 .none => break,
11808 }11820 }
11809 }11821 }
11810 if (classes[0] == .integer and classes[1] == .none) {11822 const first_non_integer = std.mem.indexOfNone(x86_64_abi.Class, &classes, &.{.integer});
11811 it.zig_index += 1;11823 if (first_non_integer == null or classes[first_non_integer.?] == .none) {
11812 it.llvm_index += 1;11824 assert(first_non_integer orelse classes.len == types_index);
11813 return .abi_sized_int;11825 if (types_index == 1) {
11826 it.zig_index += 1;
11827 it.llvm_index += 1;
11828 return .abi_sized_int;
11829 }
11830 if (mod.intern_pool.indexToKey(ty.toIntern()) == .struct_type) {
11831 var struct_it = ty.iterateStructOffsets(mod);
11832 while (struct_it.next()) |_| {}
11833 assert((std.math.divCeil(u64, struct_it.offset, 8) catch unreachable) == types_index);
11834 if (struct_it.offset % 8 > 0) types_buffer[types_index - 1] =
11835 try it.object.builder.intType(@intCast(struct_it.offset % 8 * 8));
11836 }
11814 }11837 }
11815 it.types_len = types_index;11838 it.types_len = types_index;
11816 it.types_buffer = types_buffer;11839 it.types_buffer = types_buffer;
src/codegen/llvm/Builder.zig+3-1
...@@ -572,7 +572,9 @@ pub const Type = enum(u32) {...@@ -572,7 +572,9 @@ pub const Type = enum(u32) {
572572
573 pub fn isSized(self: Type, builder: *const Builder) Allocator.Error!bool {573 pub fn isSized(self: Type, builder: *const Builder) Allocator.Error!bool {
574 var visited: IsSizedVisited = .{};574 var visited: IsSizedVisited = .{};
575 return self.isSizedVisited(&visited, builder);575 const result = try self.isSizedVisited(&visited, builder);
576 if (builder.useLibLlvm()) assert(result == self.toLlvm(builder).isSized().toBool());
577 return result;
576 }578 }
577579
578 const FormatData = struct {580 const FormatData = struct {
src/codegen/llvm/bindings.zig+3
...@@ -434,6 +434,9 @@ pub const Type = opaque {...@@ -434,6 +434,9 @@ pub const Type = opaque {
434 Packed: Bool,434 Packed: Bool,
435 ) void;435 ) void;
436436
437 pub const isSized = LLVMTypeIsSized;
438 extern fn LLVMTypeIsSized(Ty: *Type) Bool;
439
437 pub const constGEP = LLVMConstGEP2;440 pub const constGEP = LLVMConstGEP2;
438 extern fn LLVMConstGEP2(441 extern fn LLVMConstGEP2(
439 Ty: *Type,442 Ty: *Type,
test/behavior/tuple.zig+1
...@@ -459,6 +459,7 @@ test "coerce anon tuple to tuple" {...@@ -459,6 +459,7 @@ test "coerce anon tuple to tuple" {
459 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO459 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
460 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO460 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
461 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO461 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
462 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
462463
463 var x: u8 = 1;464 var x: u8 = 1;
464 var y: u16 = 2;465 var y: u16 = 2;
test/c_abi/cfuncs.c+46-4
...@@ -16,8 +16,12 @@ static void assert_or_panic(bool ok) {...@@ -16,8 +16,12 @@ static void assert_or_panic(bool ok) {
16# define ZIG_PPC3216# define ZIG_PPC32
17#endif17#endif
1818
19#if defined __riscv && defined _ILP3219#ifdef __riscv
20# define ZIG_RISCV3220# ifdef _ILP32
21# define ZIG_RISCV32
22# else
23# define ZIG_RISCV64
24# endif
21#endif25#endif
2226
23#if defined(__aarch64__) && defined(__linux__)27#if defined(__aarch64__) && defined(__linux__)
...@@ -191,6 +195,15 @@ struct SmallStructInts {...@@ -191,6 +195,15 @@ struct SmallStructInts {
191void zig_small_struct_ints(struct SmallStructInts);195void zig_small_struct_ints(struct SmallStructInts);
192struct SmallStructInts zig_ret_small_struct_ints();196struct SmallStructInts zig_ret_small_struct_ints();
193197
198struct MedStructInts {
199 int32_t x;
200 int32_t y;
201 int32_t z;
202};
203
204void zig_med_struct_ints(struct MedStructInts);
205struct MedStructInts zig_ret_med_struct_ints();
206
194struct MedStructMixed {207struct MedStructMixed {
195 uint32_t a;208 uint32_t a;
196 float b;209 float b;
...@@ -339,14 +352,22 @@ void run_c_tests(void) {...@@ -339,14 +352,22 @@ void run_c_tests(void) {
339 }352 }
340#endif353#endif
341354
342#if !defined __i386__ && !defined __arm__ && !defined __mips__ && \355#if !defined __i386__ && !defined __arm__ && !defined __aarch64__ && \
343 !defined ZIG_PPC32 && !defined _ARCH_PPC64356 !defined __mips__ && !defined __powerpc__ && !defined ZIG_RISCV64
344 {357 {
345 struct SmallStructInts s = {1, 2, 3, 4};358 struct SmallStructInts s = {1, 2, 3, 4};
346 zig_small_struct_ints(s);359 zig_small_struct_ints(s);
347 }360 }
348#endif361#endif
349362
363#if !defined __i386__ && !defined __arm__ && !defined __aarch64__ && \
364 !defined __mips__ && !defined __powerpc__ && !defined ZIG_RISCV64
365 {
366 struct MedStructInts s = {1, 2, 3};
367 zig_med_struct_ints(s);
368 }
369#endif
370
350#ifndef ZIG_NO_I128371#ifndef ZIG_NO_I128
351 {372 {
352 __int128 s = 0;373 __int128 s = 0;
...@@ -586,6 +607,27 @@ struct SmallStructInts c_ret_small_struct_ints() {...@@ -586,6 +607,27 @@ struct SmallStructInts c_ret_small_struct_ints() {
586 return s;607 return s;
587}608}
588609
610void c_med_struct_ints(struct MedStructInts s) {
611 assert_or_panic(s.x == 1);
612 assert_or_panic(s.y == 2);
613 assert_or_panic(s.z == 3);
614
615 struct MedStructInts s2 = zig_ret_med_struct_ints();
616
617 assert_or_panic(s2.x == 1);
618 assert_or_panic(s2.y == 2);
619 assert_or_panic(s2.z == 3);
620}
621
622struct MedStructInts c_ret_med_struct_ints() {
623 struct MedStructInts s = {
624 .x = 1,
625 .y = 2,
626 .z = 3,
627 };
628 return s;
629}
630
589void c_med_struct_mixed(struct MedStructMixed x) {631void c_med_struct_mixed(struct MedStructMixed x) {
590 assert_or_panic(x.a == 1234);632 assert_or_panic(x.a == 1234);
591 assert_or_panic(x.b == 100.0f);633 assert_or_panic(x.b == 100.0f);
test/c_abi/main.zig+40
...@@ -393,6 +393,38 @@ export fn zig_small_struct_ints(x: SmallStructInts) void {...@@ -393,6 +393,38 @@ export fn zig_small_struct_ints(x: SmallStructInts) void {
393 expect(x.d == 4) catch @panic("test failure");393 expect(x.d == 4) catch @panic("test failure");
394}394}
395395
396const MedStructInts = extern struct {
397 x: i32,
398 y: i32,
399 z: i32,
400};
401extern fn c_med_struct_ints(MedStructInts) void;
402extern fn c_ret_med_struct_ints() MedStructInts;
403
404test "C ABI medium struct of ints" {
405 if (builtin.cpu.arch == .x86) return error.SkipZigTest;
406 if (comptime builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
407 if (comptime builtin.cpu.arch.isPPC()) return error.SkipZigTest;
408 if (comptime builtin.cpu.arch.isPPC64()) return error.SkipZigTest;
409
410 var s = MedStructInts{
411 .x = 1,
412 .y = 2,
413 .z = 3,
414 };
415 c_med_struct_ints(s);
416 var s2 = c_ret_med_struct_ints();
417 try expect(s2.x == 1);
418 try expect(s2.y == 2);
419 try expect(s2.z == 3);
420}
421
422export fn zig_med_struct_ints(s: MedStructInts) void {
423 expect(s.x == 1) catch @panic("test failure");
424 expect(s.y == 2) catch @panic("test failure");
425 expect(s.z == 3) catch @panic("test failure");
426}
427
396const SmallPackedStruct = packed struct {428const SmallPackedStruct = packed struct {
397 a: u2,429 a: u2,
398 b: u2,430 b: u2,
...@@ -691,6 +723,14 @@ export fn zig_ret_small_struct_ints() SmallStructInts {...@@ -691,6 +723,14 @@ export fn zig_ret_small_struct_ints() SmallStructInts {
691 };723 };
692}724}
693725
726export fn zig_ret_med_struct_ints() MedStructInts {
727 return .{
728 .x = 1,
729 .y = 2,
730 .z = 3,
731 };
732}
733
694export fn zig_ret_med_struct_mixed() MedStructMixed {734export fn zig_ret_med_struct_mixed() MedStructMixed {
695 return .{735 return .{
696 .a = 1234,736 .a = 1234,
test/tests.zig+1-1
...@@ -1102,7 +1102,7 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step {...@@ -1102,7 +1102,7 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step {
1102pub fn addCAbiTests(b: *std.Build, skip_non_native: bool, skip_release: bool) *Step {1102pub fn addCAbiTests(b: *std.Build, skip_non_native: bool, skip_release: bool) *Step {
1103 const step = b.step("test-c-abi", "Run the C ABI tests");1103 const step = b.step("test-c-abi", "Run the C ABI tests");
11041104
1105 const optimize_modes: [2]OptimizeMode = .{ .Debug, .ReleaseFast };1105 const optimize_modes: [3]OptimizeMode = .{ .Debug, .ReleaseSafe, .ReleaseFast };
11061106
1107 for (optimize_modes) |optimize_mode| {1107 for (optimize_modes) |optimize_mode| {
1108 if (optimize_mode != .Debug and skip_release) continue;1108 if (optimize_mode != .Debug and skip_release) continue;