| author | |
| committer | |
| log | bde6e075dfc202fdcfa21ec9c2d90941460b002e |
| tree | fa782eaf75c76e5d7ab09ce6af58061c8e688df4 |
| parent | 423c1221f9c020a1047fc14ce8e9003d7e009914 |
| parent | 97ae2d2c29f827ebb73abbc0317cd39ac4ca4c9b |
| signature |
Fix various C ABI issues8 files changed, 143 insertions(+), 26 deletions(-)
src/Sema.zig+3-1| ... | @@ -33491,7 +33491,9 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void { | ... | @@ -33491,7 +33491,9 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void { |
| 33491 | return sema.failWithOwnedErrorMsg(msg); | 33491 | return sema.failWithOwnedErrorMsg(msg); |
| 33492 | } | 33492 | } |
| 33493 | 33493 | ||
| 33494 | if (struct_obj.layout == .Auto and mod.backendSupportsFeature(.field_reordering)) { | 33494 | if (struct_obj.layout == .Auto and !struct_obj.is_tuple and |
| 33495 | mod.backendSupportsFeature(.field_reordering)) | ||
| 33496 | { | ||
| 33495 | const optimized_order = try mod.tmp_hack_arena.allocator().alloc(u32, struct_obj.fields.count()); | 33497 | const optimized_order = try mod.tmp_hack_arena.allocator().alloc(u32, struct_obj.fields.count()); |
| 33496 | 33498 | ||
| 33497 | for (struct_obj.fields.values(), 0..) |field, i| { | 33499 | for (struct_obj.fields.values(), 0..) |field, i| { |
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 having | 5421 | // 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 callsite | 5422 | // 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 | rp | 5433 | 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) { |
| 572 | 572 | ||
| 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 | } |
| 577 | 579 | ||
| 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; |
| 436 | 436 | ||
| 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+15| ... | @@ -453,3 +453,18 @@ test "tuple pointer is indexable" { | ... | @@ -453,3 +453,18 @@ test "tuple pointer is indexable" { |
| 453 | try expectEqual(@as(u32, 100), (&y)[0]); | 453 | try expectEqual(@as(u32, 100), (&y)[0]); |
| 454 | try expectEqual(false, (&y)[1]); | 454 | try expectEqual(false, (&y)[1]); |
| 455 | } | 455 | } |
| 456 | |||
| 457 | test "coerce anon tuple to tuple" { | ||
| 458 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 459 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | ||
| 460 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | ||
| 461 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO | ||
| 462 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | ||
| 463 | |||
| 464 | var x: u8 = 1; | ||
| 465 | var y: u16 = 2; | ||
| 466 | var t = .{ x, y }; | ||
| 467 | var s: struct { u8, u16 } = t; | ||
| 468 | try expectEqual(x, s[0]); | ||
| 469 | try expectEqual(y, s[1]); | ||
| 470 | } |
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_PPC32 | 16 | # define ZIG_PPC32 |
| 17 | #endif | 17 | #endif |
| 18 | 18 | ||
| 19 | #if defined __riscv && defined _ILP32 | 19 | #ifdef __riscv |
| 20 | # define ZIG_RISCV32 | 20 | # ifdef _ILP32 |
| 21 | # define ZIG_RISCV32 | ||
| 22 | # else | ||
| 23 | # define ZIG_RISCV64 | ||
| 24 | # endif | ||
| 21 | #endif | 25 | #endif |
| 22 | 26 | ||
| 23 | #if defined(__aarch64__) && defined(__linux__) | 27 | #if defined(__aarch64__) && defined(__linux__) |
| ... | @@ -191,6 +195,15 @@ struct SmallStructInts { | ... | @@ -191,6 +195,15 @@ struct SmallStructInts { |
| 191 | void zig_small_struct_ints(struct SmallStructInts); | 195 | void zig_small_struct_ints(struct SmallStructInts); |
| 192 | struct SmallStructInts zig_ret_small_struct_ints(); | 196 | struct SmallStructInts zig_ret_small_struct_ints(); |
| 193 | 197 | ||
| 198 | struct MedStructInts { | ||
| 199 | int32_t x; | ||
| 200 | int32_t y; | ||
| 201 | int32_t z; | ||
| 202 | }; | ||
| 203 | |||
| 204 | void zig_med_struct_ints(struct MedStructInts); | ||
| 205 | struct MedStructInts zig_ret_med_struct_ints(); | ||
| 206 | |||
| 194 | struct MedStructMixed { | 207 | struct 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 | #endif | 353 | #endif |
| 341 | 354 | ||
| 342 | #if !defined __i386__ && !defined __arm__ && !defined __mips__ && \ | 355 | #if !defined __i386__ && !defined __arm__ && !defined __aarch64__ && \ |
| 343 | !defined ZIG_PPC32 && !defined _ARCH_PPC64 | 356 | !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 | #endif | 361 | #endif |
| 349 | 362 | ||
| 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_I128 | 371 | #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 | } |
| 588 | 609 | ||
| 610 | void 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 | |||
| 622 | struct 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 | |||
| 589 | void c_med_struct_mixed(struct MedStructMixed x) { | 631 | void 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-10| ... | @@ -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 | } |
| 395 | 395 | ||
| 396 | const MedStructInts = extern struct { | ||
| 397 | x: i32, | ||
| 398 | y: i32, | ||
| 399 | z: i32, | ||
| 400 | }; | ||
| 401 | extern fn c_med_struct_ints(MedStructInts) void; | ||
| 402 | extern fn c_ret_med_struct_ints() MedStructInts; | ||
| 403 | |||
| 404 | test "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 | |||
| 422 | export 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 | |||
| 396 | const SmallPackedStruct = packed struct { | 428 | const 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 | } |
| 693 | 725 | ||
| 726 | export fn zig_ret_med_struct_ints() MedStructInts { | ||
| 727 | return .{ | ||
| 728 | .x = 1, | ||
| 729 | .y = 2, | ||
| 730 | .z = 3, | ||
| 731 | }; | ||
| 732 | } | ||
| 733 | |||
| 694 | export fn zig_ret_med_struct_mixed() MedStructMixed { | 734 | export fn zig_ret_med_struct_mixed() MedStructMixed { |
| 695 | return .{ | 735 | return .{ |
| 696 | .a = 1234, | 736 | .a = 1234, |
| ... | @@ -813,11 +853,6 @@ extern fn c_ret_medium_vec() MediumVec; | ... | @@ -813,11 +853,6 @@ extern fn c_ret_medium_vec() MediumVec; |
| 813 | test "medium simd vector" { | 853 | test "medium simd vector" { |
| 814 | if (comptime builtin.cpu.arch.isPPC64()) return error.SkipZigTest; | 854 | if (comptime builtin.cpu.arch.isPPC64()) return error.SkipZigTest; |
| 815 | 855 | ||
| 816 | if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .linux) { | ||
| 817 | // TODO: https://github.com/ziglang/zig/issues/14908 | ||
| 818 | return error.SkipZigTest; | ||
| 819 | } | ||
| 820 | |||
| 821 | c_medium_vec(.{ 1, 2, 3, 4 }); | 856 | c_medium_vec(.{ 1, 2, 3, 4 }); |
| 822 | 857 | ||
| 823 | var x = c_ret_medium_vec(); | 858 | var x = c_ret_medium_vec(); |
| ... | @@ -837,11 +872,6 @@ test "big simd vector" { | ... | @@ -837,11 +872,6 @@ test "big simd vector" { |
| 837 | if (comptime builtin.cpu.arch.isPPC64()) return error.SkipZigTest; | 872 | if (comptime builtin.cpu.arch.isPPC64()) return error.SkipZigTest; |
| 838 | if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .macos and builtin.mode != .Debug) return error.SkipZigTest; | 873 | if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .macos and builtin.mode != .Debug) return error.SkipZigTest; |
| 839 | 874 | ||
| 840 | if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .linux) { | ||
| 841 | // TODO: https://github.com/ziglang/zig/issues/14908 | ||
| 842 | return error.SkipZigTest; | ||
| 843 | } | ||
| 844 | |||
| 845 | c_big_vec(.{ 1, 2, 3, 4, 5, 6, 7, 8 }); | 875 | c_big_vec(.{ 1, 2, 3, 4, 5, 6, 7, 8 }); |
| 846 | 876 | ||
| 847 | var x = c_ret_big_vec(); | 877 | var x = c_ret_big_vec(); |
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 { |
| 1102 | pub fn addCAbiTests(b: *std.Build, skip_non_native: bool, skip_release: bool) *Step { | 1102 | pub 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"); |
| 1104 | 1104 | ||
| 1105 | const optimize_modes: [2]OptimizeMode = .{ .Debug, .ReleaseFast }; | 1105 | const optimize_modes: [3]OptimizeMode = .{ .Debug, .ReleaseSafe, .ReleaseFast }; |
| 1106 | 1106 | ||
| 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; |