authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-09 13:23:20-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-02-09 13:23:20-05:00
logdb56d74a3bc9c6240d24838753a7042ba91501a1
treed5205539c16c3c22e31b18c5cb01efb929b6f308
parent92cb17a331b0f2f3a89b8e5e6995e8e36b9f3679
parent77a6031edbd17dfdf654f24d139f27ac7adeb82f
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #10834 from ziglang/fix-x86-i128-c-abi

stage1: fix x86 i128 C ABI for extern structs

5 files changed, 166 insertions(+), 51 deletions(-)

src/arch/x86_64/abi.zig+27-3
......@@ -18,10 +18,34 @@ pub fn classifyWindows(ty: Type, target: Target) Class {
1818 else => return .memory,
1919 }
2020 return switch (ty.zigTypeTag()) {
21 .Int, .Bool, .Enum, .Void, .NoReturn, .ErrorSet, .Struct, .Union => .integer,
22 .Optional => if (ty.isPtrLikeOptional()) return .integer else return .memory,
21 .Pointer,
22 .Int,
23 .Bool,
24 .Enum,
25 .Void,
26 .NoReturn,
27 .ErrorSet,
28 .Struct,
29 .Union,
30 .Optional,
31 .Array,
32 .ErrorUnion,
33 .AnyFrame,
34 .Frame,
35 => .integer,
36
2337 .Float, .Vector => .sse,
24 else => unreachable,
38
39 .Type,
40 .ComptimeFloat,
41 .ComptimeInt,
42 .Undefined,
43 .Null,
44 .BoundFn,
45 .Fn,
46 .Opaque,
47 .EnumLiteral,
48 => unreachable,
2549 };
2650}
2751
src/stage1/analyze.cpp+61-16
......@@ -8279,23 +8279,53 @@ Error file_fetch(CodeGen *g, Buf *resolved_path, Buf *contents_buf) {
82798279
82808280static X64CABIClass type_windows_abi_x86_64_class(CodeGen *g, ZigType *ty, size_t ty_size) {
82818281 // https://docs.microsoft.com/en-gb/cpp/build/x64-calling-convention?view=vs-2017
8282 switch (ty_size) {
8283 case 1:
8284 case 2:
8285 case 4:
8286 case 8:
8287 break;
8288 case 16:
8289 return (ty->id == ZigTypeIdVector) ? X64CABIClass_SSE : X64CABIClass_MEMORY;
8290 default:
8291 return X64CABIClass_MEMORY;
8292 }
82828293 switch (ty->id) {
8283 case ZigTypeIdEnum:
8294 case ZigTypeIdInvalid:
8295 case ZigTypeIdMetaType:
8296 case ZigTypeIdComptimeFloat:
8297 case ZigTypeIdComptimeInt:
8298 case ZigTypeIdNull:
8299 case ZigTypeIdUndefined:
8300 case ZigTypeIdBoundFn:
8301 case ZigTypeIdOpaque:
8302 case ZigTypeIdEnumLiteral:
8303 zig_unreachable();
8304
8305 case ZigTypeIdFn:
8306 case ZigTypeIdPointer:
82848307 case ZigTypeIdInt:
82858308 case ZigTypeIdBool:
8309 case ZigTypeIdEnum:
8310 case ZigTypeIdVoid:
8311 case ZigTypeIdUnreachable:
8312 case ZigTypeIdErrorSet:
8313 case ZigTypeIdErrorUnion:
8314 case ZigTypeIdStruct:
8315 case ZigTypeIdUnion:
8316 case ZigTypeIdOptional:
8317 case ZigTypeIdFnFrame:
8318 case ZigTypeIdAnyFrame:
82868319 return X64CABIClass_INTEGER;
8320
82878321 case ZigTypeIdFloat:
82888322 case ZigTypeIdVector:
82898323 return X64CABIClass_SSE;
8290 case ZigTypeIdStruct:
8291 case ZigTypeIdUnion: {
8292 if (ty_size <= 8)
8293 return X64CABIClass_INTEGER;
8294 return X64CABIClass_MEMORY;
8295 }
8296 default:
8324
8325 case ZigTypeIdArray:
82978326 return X64CABIClass_Unknown;
82988327 }
8328 zig_unreachable();
82998329}
83008330
83018331static X64CABIClass type_system_V_abi_x86_64_class(CodeGen *g, ZigType *ty, size_t ty_size) {
......@@ -8374,17 +8404,19 @@ static X64CABIClass type_system_V_abi_x86_64_class(CodeGen *g, ZigType *ty, size
83748404
83758405X64CABIClass type_c_abi_x86_64_class(CodeGen *g, ZigType *ty) {
83768406 Error err;
8377
83788407 const size_t ty_size = type_size(g, ty);
8408
8409 if (g->zig_target->os == OsWindows || g->zig_target->os == OsUefi) {
8410 return type_windows_abi_x86_64_class(g, ty, ty_size);
8411 }
8412
83798413 ZigType *ptr_type;
83808414 if ((err = get_codegen_ptr_type(g, ty, &ptr_type))) return X64CABIClass_Unknown;
83818415 if (ptr_type != nullptr)
83828416 return X64CABIClass_INTEGER;
83838417
8384 if (g->zig_target->os == OsWindows || g->zig_target->os == OsUefi) {
8385 return type_windows_abi_x86_64_class(g, ty, ty_size);
8386 } else if (g->zig_target->arch == ZigLLVM_aarch64 ||
8387 g->zig_target->arch == ZigLLVM_aarch64_be)
8418 if (g->zig_target->arch == ZigLLVM_aarch64 ||
8419 g->zig_target->arch == ZigLLVM_aarch64_be)
83888420 {
83898421 X64CABIClass result = type_system_V_abi_x86_64_class(g, ty, ty_size);
83908422 return (result == X64CABIClass_MEMORY) ? X64CABIClass_MEMORY_nobyval : result;
......@@ -8684,14 +8716,23 @@ static Error resolve_llvm_c_abi_type(CodeGen *g, ZigType *ty) {
86848716 if (ty->data.structure.fields[i]->offset >= 8) {
86858717 eightbyte_index = 1;
86868718 }
8687 X64CABIClass field_class = type_c_abi_x86_64_class(g, ty->data.structure.fields[i]->type_entry);
8719 ZigType *field_ty = ty->data.structure.fields[i]->type_entry;
8720 X64CABIClass field_class = type_c_abi_x86_64_class(g, field_ty);
86888721
86898722 if (field_class == X64CABIClass_INTEGER) {
86908723 type_classes[eightbyte_index] = X64CABIClass_INTEGER;
86918724 } else if (type_classes[eightbyte_index] == X64CABIClass_Unknown) {
86928725 type_classes[eightbyte_index] = field_class;
86938726 }
8694 type_sizes[eightbyte_index] += ty->data.structure.fields[i]->type_entry->abi_size;
8727 if (field_ty->abi_size > 8) {
8728 assert(eightbyte_index == 0);
8729 type_sizes[0] = 8;
8730 type_sizes[1] = field_ty->abi_size - 8;
8731 type_classes[1] = type_classes[0];
8732 eightbyte_index = 1;
8733 } else {
8734 type_sizes[eightbyte_index] += field_ty->abi_size;
8735 }
86958736 }
86968737
86978738 LLVMTypeRef return_elem_types[] = {
......@@ -8980,8 +9021,12 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS
89809021 struct_type->data.structure.llvm_full_type_queue_index = SIZE_MAX;
89819022 }
89829023
8983 if (struct_type->abi_size <= 16 && (struct_type->data.structure.layout == ContainerLayoutExtern || struct_type->data.structure.layout == ContainerLayoutPacked))
9024 if (struct_type->abi_size <= 16 &&
9025 (struct_type->data.structure.layout == ContainerLayoutExtern ||
9026 struct_type->data.structure.layout == ContainerLayoutPacked))
9027 {
89849028 resolve_llvm_c_abi_type(g, struct_type);
9029 }
89859030}
89869031
89879032// This is to be used instead of void for debug info types, to avoid tripping
test/stage1/c_abi/build.zig+3
......@@ -2,15 +2,18 @@ const Builder = @import("std").build.Builder;
22
33pub fn build(b: *Builder) void {
44 const rel_opts = b.standardReleaseOptions();
5 const target = b.standardTargetOptions(.{});
56
67 const c_obj = b.addObject("cfuncs", null);
78 c_obj.addCSourceFile("cfuncs.c", &[_][]const u8{"-std=c99"});
89 c_obj.setBuildMode(rel_opts);
910 c_obj.linkSystemLibrary("c");
11 c_obj.target = target;
1012
1113 const main = b.addTest("main.zig");
1214 main.setBuildMode(rel_opts);
1315 main.addObject(c_obj);
16 main.target = target;
1417
1518 const test_step = b.step("test", "Test the program");
1619 test_step.dependOn(&main.step);
test/stage1/c_abi/cfuncs.c+26
......@@ -11,14 +11,24 @@ static void assert_or_panic(bool ok) {
1111 }
1212}
1313
14struct i128 {
15 __int128 value;
16};
17
18struct u128 {
19 unsigned __int128 value;
20};
21
1422void zig_u8(uint8_t);
1523void zig_u16(uint16_t);
1624void zig_u32(uint32_t);
1725void zig_u64(uint64_t);
26void zig_struct_u128(struct u128);
1827void zig_i8(int8_t);
1928void zig_i16(int16_t);
2029void zig_i32(int32_t);
2130void zig_i64(int64_t);
31void zig_struct_i128(struct i128);
2232void zig_five_integers(int32_t, int32_t, int32_t, int32_t, int32_t);
2333
2434void zig_f32(float);
......@@ -130,11 +140,19 @@ void run_c_tests(void) {
130140 zig_u16(0xfffe);
131141 zig_u32(0xfffffffd);
132142 zig_u64(0xfffffffffffffffc);
143 {
144 struct u128 s = {0xfffffffffffffffc};
145 zig_struct_u128(s);
146 }
133147
134148 zig_i8(-1);
135149 zig_i16(-2);
136150 zig_i32(-3);
137151 zig_i64(-4);
152 {
153 struct i128 s = {-6};
154 zig_struct_i128(s);
155 }
138156 zig_five_integers(12, 34, 56, 78, 90);
139157
140158 zig_f32(12.34f);
......@@ -221,6 +239,10 @@ void c_u64(uint64_t x) {
221239 assert_or_panic(x == 0xfffffffffffffffcULL);
222240}
223241
242void c_struct_u128(struct u128 x) {
243 assert_or_panic(x.value == 0xfffffffffffffffcULL);
244}
245
224246void c_i8(int8_t x) {
225247 assert_or_panic(x == -1);
226248}
......@@ -237,6 +259,10 @@ void c_i64(int64_t x) {
237259 assert_or_panic(x == -4);
238260}
239261
262void c_struct_i128(struct i128 x) {
263 assert_or_panic(x.value == -6);
264}
265
240266void c_f32(float x) {
241267 assert_or_panic(x == 12.34f);
242268}
test/stage1/c_abi/main.zig+49-32
......@@ -16,20 +16,22 @@ extern fn c_u8(u8) void;
1616extern fn c_u16(u16) void;
1717extern fn c_u32(u32) void;
1818extern fn c_u64(u64) void;
19extern fn c_struct_u128(U128) void;
1920extern fn c_i8(i8) void;
2021extern fn c_i16(i16) void;
2122extern fn c_i32(i32) void;
2223extern fn c_i64(i64) void;
24extern fn c_struct_i128(I128) void;
2325
2426// On windows x64, the first 4 are passed via registers, others on the stack.
2527extern fn c_five_integers(i32, i32, i32, i32, i32) void;
2628
2729export fn zig_five_integers(a: i32, b: i32, c: i32, d: i32, e: i32) void {
28 expect(a == 12) catch @panic("test failure");
29 expect(b == 34) catch @panic("test failure");
30 expect(c == 56) catch @panic("test failure");
31 expect(d == 78) catch @panic("test failure");
32 expect(e == 90) catch @panic("test failure");
30 expect(a == 12) catch @panic("test failure: zig_five_integers 12");
31 expect(b == 34) catch @panic("test failure: zig_five_integers 34");
32 expect(c == 56) catch @panic("test failure: zig_five_integers 56");
33 expect(d == 78) catch @panic("test failure: zig_five_integers 78");
34 expect(e == 90) catch @panic("test failure: zig_five_integers 90");
3335}
3436
3537test "C ABI integers" {
......@@ -37,37 +39,52 @@ test "C ABI integers" {
3739 c_u16(0xfffe);
3840 c_u32(0xfffffffd);
3941 c_u64(0xfffffffffffffffc);
42 c_struct_u128(.{ .value = 0xfffffffffffffffc });
4043
4144 c_i8(-1);
4245 c_i16(-2);
4346 c_i32(-3);
4447 c_i64(-4);
48 c_struct_i128(.{ .value = -6 });
4549 c_five_integers(12, 34, 56, 78, 90);
4650}
4751
4852export fn zig_u8(x: u8) void {
49 expect(x == 0xff) catch @panic("test failure");
53 expect(x == 0xff) catch @panic("test failure: zig_u8");
5054}
5155export fn zig_u16(x: u16) void {
52 expect(x == 0xfffe) catch @panic("test failure");
56 expect(x == 0xfffe) catch @panic("test failure: zig_u16");
5357}
5458export fn zig_u32(x: u32) void {
55 expect(x == 0xfffffffd) catch @panic("test failure");
59 expect(x == 0xfffffffd) catch @panic("test failure: zig_u32");
5660}
5761export fn zig_u64(x: u64) void {
58 expect(x == 0xfffffffffffffffc) catch @panic("test failure");
62 expect(x == 0xfffffffffffffffc) catch @panic("test failure: zig_u64");
5963}
6064export fn zig_i8(x: i8) void {
61 expect(x == -1) catch @panic("test failure");
65 expect(x == -1) catch @panic("test failure: zig_i8");
6266}
6367export fn zig_i16(x: i16) void {
64 expect(x == -2) catch @panic("test failure");
68 expect(x == -2) catch @panic("test failure: zig_i16");
6569}
6670export fn zig_i32(x: i32) void {
67 expect(x == -3) catch @panic("test failure");
71 expect(x == -3) catch @panic("test failure: zig_i32");
6872}
6973export fn zig_i64(x: i64) void {
70 expect(x == -4) catch @panic("test failure");
74 expect(x == -4) catch @panic("test failure: zig_i64");
75}
76
77const I128 = extern struct {
78 value: i128,
79};
80const U128 = extern struct {
81 value: u128,
82};
83export fn zig_struct_i128(a: I128) void {
84 expect(a.value == -6) catch @panic("test failure: zig_struct_i128");
85}
86export fn zig_struct_u128(a: U128) void {
87 expect(a.value == 0xfffffffffffffffc) catch @panic("test failure: zig_struct_u128");
7188}
7289
7390extern fn c_f32(f32) void;
......@@ -77,11 +94,11 @@ extern fn c_f64(f64) void;
7794extern fn c_five_floats(f32, f32, f32, f32, f32) void;
7895
7996export fn zig_five_floats(a: f32, b: f32, c: f32, d: f32, e: f32) void {
80 expect(a == 1.0) catch @panic("test failure");
81 expect(b == 2.0) catch @panic("test failure");
82 expect(c == 3.0) catch @panic("test failure");
83 expect(d == 4.0) catch @panic("test failure");
84 expect(e == 5.0) catch @panic("test failure");
97 expect(a == 1.0) catch @panic("test failure: zig_five_floats 1.0");
98 expect(b == 2.0) catch @panic("test failure: zig_five_floats 2.0");
99 expect(c == 3.0) catch @panic("test failure: zig_five_floats 3.0");
100 expect(d == 4.0) catch @panic("test failure: zig_five_floats 4.0");
101 expect(e == 5.0) catch @panic("test failure: zig_five_floats 5.0");
85102}
86103
87104test "C ABI floats" {
......@@ -91,10 +108,10 @@ test "C ABI floats" {
91108}
92109
93110export fn zig_f32(x: f32) void {
94 expect(x == 12.34) catch @panic("test failure");
111 expect(x == 12.34) catch @panic("test failure: zig_f32");
95112}
96113export fn zig_f64(x: f64) void {
97 expect(x == 56.78) catch @panic("test failure");
114 expect(x == 56.78) catch @panic("test failure: zig_f64");
98115}
99116
100117extern fn c_ptr(*anyopaque) void;
......@@ -104,7 +121,7 @@ test "C ABI pointer" {
104121}
105122
106123export fn zig_ptr(x: *anyopaque) void {
107 expect(@ptrToInt(x) == 0xdeadbeef) catch @panic("test failure");
124 expect(@ptrToInt(x) == 0xdeadbeef) catch @panic("test failure: zig_ptr");
108125}
109126
110127extern fn c_bool(bool) void;
......@@ -114,7 +131,7 @@ test "C ABI bool" {
114131}
115132
116133export fn zig_bool(x: bool) void {
117 expect(x) catch @panic("test failure");
134 expect(x) catch @panic("test failure: zig_bool");
118135}
119136
120137const BigStruct = extern struct {
......@@ -138,11 +155,11 @@ test "C ABI big struct" {
138155}
139156
140157export fn zig_big_struct(x: BigStruct) void {
141 expect(x.a == 1) catch @panic("test failure");
142 expect(x.b == 2) catch @panic("test failure");
143 expect(x.c == 3) catch @panic("test failure");
144 expect(x.d == 4) catch @panic("test failure");
145 expect(x.e == 5) catch @panic("test failure");
158 expect(x.a == 1) catch @panic("test failure: zig_big_struct 1");
159 expect(x.b == 2) catch @panic("test failure: zig_big_struct 2");
160 expect(x.c == 3) catch @panic("test failure: zig_big_struct 3");
161 expect(x.d == 4) catch @panic("test failure: zig_big_struct 4");
162 expect(x.e == 5) catch @panic("test failure: zig_big_struct 5");
146163}
147164
148165const BigUnion = extern union {
......@@ -164,11 +181,11 @@ test "C ABI big union" {
164181}
165182
166183export fn zig_big_union(x: BigUnion) void {
167 expect(x.a.a == 1) catch @panic("test failure");
168 expect(x.a.b == 2) catch @panic("test failure");
169 expect(x.a.c == 3) catch @panic("test failure");
170 expect(x.a.d == 4) catch @panic("test failure");
171 expect(x.a.e == 5) catch @panic("test failure");
184 expect(x.a.a == 1) catch @panic("test failure: zig_big_union a");
185 expect(x.a.b == 2) catch @panic("test failure: zig_big_union b");
186 expect(x.a.c == 3) catch @panic("test failure: zig_big_union c");
187 expect(x.a.d == 4) catch @panic("test failure: zig_big_union d");
188 expect(x.a.e == 5) catch @panic("test failure: zig_big_union e");
172189}
173190
174191const MedStructMixed = extern struct {