authorgravatar for teflate@icloud.comteflate <teflate@icloud.com> 2026-06-07 18:48:38+03:00
committergravatar for teflate@icloud.comteflate <teflate@icloud.com> 2026-06-07 23:35:17+03:00
logb95b61e4186adbf288a6dabb86b1f5450f45d3ab
treea161723e00d908df468562f976c809992cbf6408
parent23f46bcd4d1d6cca5a25ed9e7b1222b1283de7af

llvm: fix return type lowering of x86 fastcall


3 files changed, 236 insertions(+), 8 deletions(-)

src/codegen/llvm/FuncGen.zig+30-1
......@@ -6944,6 +6944,7 @@ pub fn firstParamSRet(fn_info: InternPool.Key.FuncType, zcu: *Zcu, target: *cons
69446944 .x86_64_win => x86_64_abi.classifyWindows(return_type, zcu, target, .ret) == .memory,
69456945 .x86_sysv, .x86_win => isByRef(return_type, zcu),
69466946 .x86_stdcall => !isScalar(zcu, return_type),
6947 .x86_fastcall => firstParamSRetX86Fastcall(zcu, return_type),
69476948 .wasm_mvp => wasm_c_abi.classifyType(return_type, zcu) == .indirect,
69486949 .aarch64_aapcs,
69496950 .aarch64_aapcs_darwin,
......@@ -6963,6 +6964,20 @@ pub fn firstParamSRet(fn_info: InternPool.Key.FuncType, zcu: *Zcu, target: *cons
69636964 };
69646965}
69656966
6967fn firstParamSRetX86Fastcall(zcu: *Zcu, ty: Type) bool {
6968 if (isScalar(zcu, ty)) {
6969 return false;
6970 }
6971 const tag = ty.zigTypeTag(zcu);
6972 if (tag == .@"struct" or tag == .@"union") {
6973 const size = ty.abiSize(zcu);
6974 if (size == 1 or size == 2 or size == 4 or size == 8) {
6975 return false;
6976 }
6977 }
6978 return true;
6979}
6980
69666981fn firstParamSRetSystemV(ty: Type, zcu: *Zcu, target: *const std.Target) bool {
69676982 const class = x86_64_abi.classifySystemV(ty, zcu, target, .ret);
69686983 if (class[0] == .memory) return true;
......@@ -6984,10 +6999,10 @@ pub fn lowerFnRetTy(o: *Object, fn_info: InternPool.Key.FuncType) Allocator.Erro
69846999 switch (fn_info.cc) {
69857000 .@"inline" => unreachable,
69867001 .auto => return if (returnTypeByRef(zcu, target, return_type)) .void else o.lowerType(return_type),
6987
69887002 .x86_64_sysv => return lowerSystemVFnRetTy(o, fn_info),
69897003 .x86_64_win => return lowerWin64FnRetTy(o, fn_info),
69907004 .x86_stdcall => return if (isScalar(zcu, return_type)) o.lowerType(return_type) else .void,
7005 .x86_fastcall => return lowerX86FastcallFnRetTy(o, zcu, return_type),
69917006 .x86_sysv, .x86_win => return if (isByRef(return_type, zcu)) .void else o.lowerType(return_type),
69927007 .aarch64_aapcs, .aarch64_aapcs_darwin, .aarch64_aapcs_win => switch (aarch64_c_abi.classifyType(return_type, zcu)) {
69937008 .memory => return .void,
......@@ -7038,6 +7053,20 @@ pub fn lowerFnRetTy(o: *Object, fn_info: InternPool.Key.FuncType) Allocator.Erro
70387053 }
70397054}
70407055
7056fn lowerX86FastcallFnRetTy(o: *Object, zcu: *Zcu, ty: Type) Allocator.Error!Builder.Type {
7057 if (isScalar(zcu, ty)) {
7058 return o.lowerType(ty);
7059 }
7060 const tag = ty.zigTypeTag(zcu);
7061 if (tag == .@"struct" or tag == .@"union") {
7062 const size = ty.abiSize(zcu);
7063 if (size == 1 or size == 2 or size == 4 or size == 8) {
7064 return o.builder.intType(@intCast(size * 8));
7065 }
7066 }
7067 return .void;
7068}
7069
70417070fn lowerWin64FnRetTy(o: *Object, fn_info: InternPool.Key.FuncType) Allocator.Error!Builder.Type {
70427071 const zcu = o.zcu;
70437072 const return_type = Type.fromInterned(fn_info.return_type);
test/c_abi/cfuncs.c+102-3
......@@ -153,6 +153,10 @@ static void assert_or_panic(bool ok) {
153153#define ZIG_NO_F128
154154#endif
155155
156#ifdef _MSC_VER
157#define ZIG_NO_F128
158#endif
159
156160#ifndef ZIG_NO_I128
157161struct i128 {
158162 __int128 value;
......@@ -198,12 +202,14 @@ void zig_ptr(void *);
198202
199203void zig_bool(bool);
200204
205#ifndef ZIG_NO_COMPLEX
201206// Note: These two functions match the signature of __mulsc3 and __muldc3 in compiler-rt (and libgcc)
202207float complex zig_cmultf_comp(float a_r, float a_i, float b_r, float b_i);
203208double complex zig_cmultd_comp(double a_r, double a_i, double b_r, double b_i);
204209
205210float complex zig_cmultf(float complex a, float complex b);
206211double complex zig_cmultd(double complex a, double complex b);
212#endif
207213
208214struct Struct_u8 {
209215 uint8_t a;
......@@ -5312,6 +5318,7 @@ void c_five_floats(float a, float b, float c, float d, float e) {
53125318 assert_or_panic(e == 5.0);
53135319}
53145320
5321#ifndef ZIG_NO_COMPLEX
53155322float complex c_cmultf_comp(float a_r, float a_i, float b_r, float b_i) {
53165323 assert_or_panic(a_r == 1.25f);
53175324 assert_or_panic(a_i == 2.6f);
......@@ -5347,6 +5354,7 @@ double complex c_cmultd(double complex a, double complex b) {
53475354
53485355 return 1.5 + I * 13.5;
53495356}
5357#endif
53505358
53515359struct Struct_i32_i32 c_mut_struct_i32_i32(struct Struct_i32_i32 s) {
53525360 assert_or_panic(s.a == 1);
......@@ -5792,7 +5800,7 @@ f16_struct c_f16_struct(f16_struct a) {
57925800 return (f16_struct){34};
57935801}
57945802
5795#if defined __x86_64__ || defined __i386__
5803#if (defined __x86_64__ || defined __i386__) && !defined _MSC_VER
57965804typedef long double f80;
57975805f80 c_f80(f80 a) {
57985806 assert_or_panic((double)a == 12.34);
......@@ -5907,7 +5915,7 @@ double c_byval_tail_callsite_attr(struct byval_tail_callsite_attr_Rect in) {
59075915 return in.size.width;
59085916}
59095917
5910#ifdef __i386__
5918#if defined(__i386__) && defined(_WIN32) && !defined(_WIN64) && defined(_MSC_VER)
59115919void __attribute__((fastcall)) zig_fastcall_check(int a, float b, void *c, double d, int e);
59125920void __attribute__((fastcall)) c_fastcall_check(int a, float b, void *c, double d, int e) {
59135921 assert_or_panic(a == 1);
......@@ -5915,7 +5923,98 @@ void __attribute__((fastcall)) c_fastcall_check(int a, float b, void *c, double
59155923 assert_or_panic((uintptr_t)c == 3);
59165924 assert_or_panic(d == 4.0);
59175925 assert_or_panic(e == 5);
5918 zig_fastcall_check(a, b, c, d, e);
5926}
5927
5928typedef struct {
5929 int a;
5930 int b;
5931 int c;
5932} FastcallSRet;
5933FastcallSRet __attribute__((fastcall)) zig_fastcall_sret(void);
5934FastcallSRet __attribute__((fastcall)) c_fastcall_sret(void) {
5935 return (FastcallSRet){
5936 .a = 1,
5937 .b = 2,
5938 .c = 3
5939 };
5940}
5941
5942typedef struct {
5943 char a;
5944 short b;
5945} FastcallNoSRet;
5946FastcallNoSRet __attribute__((fastcall)) zig_fastcall_no_sret(void);
5947FastcallNoSRet __attribute__((fastcall)) c_fastcall_no_sret(void) {
5948 return (FastcallNoSRet){
5949 .a = 1,
5950 .b = 2
5951 };
5952}
5953
5954typedef struct {
5955 float a;
5956 float b;
5957} FastcallNoSRetF32F32;
5958FastcallNoSRetF32F32 __attribute__((fastcall)) zig_fastcall_no_sret_f32_f32(void);
5959FastcallNoSRetF32F32 __attribute__((fastcall)) c_fastcall_no_sret_f32_f32(void) {
5960 return (FastcallNoSRetF32F32){
5961 .a = 1,
5962 .b = 2
5963 };
5964}
5965
5966typedef struct {
5967 double a;
5968} FastcallNoSRetF64;
5969FastcallNoSRetF64 __attribute__((fastcall)) zig_fastcall_no_sret_f64(void);
5970FastcallNoSRetF64 __attribute__((fastcall)) c_fastcall_no_sret_f64(void) {
5971 return (FastcallNoSRetF64){
5972 .a = 1
5973 };
5974}
5975
5976float __attribute__((fastcall)) zig_fastcall_ret_f32(void);
5977float __attribute__((fastcall)) c_fastcall_ret_f32(void) {
5978 return 1;
5979}
5980
5981double __attribute__((fastcall)) zig_fastcall_ret_f64(void);
5982double __attribute__((fastcall)) c_fastcall_ret_f64(void) {
5983 return 1;
5984}
5985
5986void run_c_fastcall_tests(void) {
5987 {
5988 zig_fastcall_check(1, 2, (void*)3, 4, 5);
5989 }
5990 {
5991 const FastcallSRet s = zig_fastcall_sret();
5992 assert_or_panic(s.a == 1);
5993 assert_or_panic(s.b == 2);
5994 assert_or_panic(s.c == 3);
5995 }
5996 {
5997 const FastcallNoSRet s = zig_fastcall_no_sret();
5998 assert_or_panic(s.a == 1);
5999 assert_or_panic(s.b == 2);
6000 }
6001 {
6002 const FastcallNoSRetF32F32 s = zig_fastcall_no_sret_f32_f32();
6003 assert_or_panic(s.a == 1);
6004 assert_or_panic(s.b == 2);
6005 }
6006 {
6007 const FastcallNoSRetF64 s = zig_fastcall_no_sret_f64();
6008 assert_or_panic(s.a == 1);
6009 }
6010 {
6011 const float s = zig_fastcall_ret_f32();
6012 assert_or_panic(s == 1);
6013 }
6014 {
6015 const double s = zig_fastcall_ret_f64();
6016 assert_or_panic(s == 1);
6017 }
59196018}
59206019
59216020void __attribute__((vectorcall)) zig_vectorcall_check(int a, float b, double c, void *d, float e, double f, double g, float h, float i, int j);
test/c_abi/main.zig+104-4
......@@ -15,8 +15,8 @@ const have_i128 = builtin.cpu.arch != .x86 and !builtin.cpu.arch.isArm() and
1515 builtin.cpu.arch != .hexagon and
1616 builtin.cpu.arch != .s390x; // https://github.com/llvm/llvm-project/issues/168460
1717
18const have_f128 = builtin.cpu.arch.isWasm() or (builtin.cpu.arch.isX86() and !builtin.os.tag.isDarwin());
19const have_f80 = builtin.cpu.arch.isX86();
18const have_f128 = builtin.cpu.arch.isWasm() or (builtin.cpu.arch.isX86() and !builtin.os.tag.isDarwin() and builtin.abi != .msvc);
19const have_f80 = builtin.cpu.arch.isX86() and builtin.abi != .msvc;
2020
2121extern fn run_c_tests() void;
2222
......@@ -6162,21 +6162,121 @@ test "byval tail callsite attribute" {
61626162
61636163test "x86 fastcall calling convention" {
61646164 if (builtin.cpu.arch != .x86) return error.SkipZigTest;
6165 if (builtin.os.tag != .windows) return error.SkipZigTest;
6166 if (builtin.abi != .msvc) return error.SkipZigTest;
6167
61656168 const static = struct {
6166 extern fn c_fastcall_check(a: c_int, b: f32, c: *anyopaque, d: f64, e: c_int) callconv(.{ .x86_fastcall = .{} }) void;
6167 export fn zig_fastcall_check(a: c_int, b: f32, c: *anyopaque, d: f64, e: c_int) callconv(.{ .x86_fastcall = .{} }) void {
6169 const fastcall: std.builtin.CallingConvention = .{ .x86_fastcall = .{} };
6170
6171 extern fn c_fastcall_check(a: c_int, b: f32, c: *anyopaque, d: f64, e: c_int) callconv(fastcall) void;
6172 export fn zig_fastcall_check(a: c_int, b: f32, c: *anyopaque, d: f64, e: c_int) callconv(fastcall) void {
61686173 if (a != 1) @panic("test failure");
61696174 if (b != 2.0) @panic("test failure");
61706175 if (@intFromPtr(c) != 3) @panic("test failure");
61716176 if (d != 4.0) @panic("test failure");
61726177 if (e != 5) @panic("test failure");
61736178 }
6179
6180 const SRet = extern struct {
6181 a: i32,
6182 b: i32,
6183 c: i32,
6184 };
6185 extern fn c_fastcall_sret() callconv(fastcall) SRet;
6186 export fn zig_fastcall_sret() callconv(fastcall) SRet {
6187 return .{
6188 .a = 1,
6189 .b = 2,
6190 .c = 3,
6191 };
6192 }
6193
6194 const NoSRet = extern struct {
6195 a: i8,
6196 b: i16,
6197 };
6198 extern fn c_fastcall_no_sret() callconv(fastcall) NoSRet;
6199 export fn zig_fastcall_no_sret() callconv(fastcall) NoSRet {
6200 return .{
6201 .a = 1,
6202 .b = 2,
6203 };
6204 }
6205
6206 const NoSRetF32F32 = extern struct {
6207 a: f32,
6208 b: f32,
6209 };
6210 extern fn c_fastcall_no_sret_f32_f32() callconv(fastcall) NoSRetF32F32;
6211 export fn zig_fastcall_no_sret_f32_f32() callconv(fastcall) NoSRetF32F32 {
6212 return .{
6213 .a = 1,
6214 .b = 2,
6215 };
6216 }
6217
6218 const NoSRetF64 = extern struct {
6219 a: f64,
6220 };
6221 extern fn c_fastcall_no_sret_f64() callconv(fastcall) NoSRetF64;
6222 export fn zig_fastcall_no_sret_f64() callconv(fastcall) NoSRetF64 {
6223 return .{
6224 .a = 1,
6225 };
6226 }
6227
6228 extern fn c_fastcall_ret_f32() callconv(fastcall) f32;
6229 export fn zig_fastcall_ret_f32() callconv(fastcall) f32 {
6230 return 1;
6231 }
6232
6233 extern fn c_fastcall_ret_f64() callconv(fastcall) f64;
6234 export fn zig_fastcall_ret_f64() callconv(fastcall) f64 {
6235 return 1;
6236 }
6237
6238 extern fn run_c_fastcall_tests() void;
61746239 };
6240
61756241 static.c_fastcall_check(1, 2.0, @ptrFromInt(3), 4.0, 5);
6242
6243 {
6244 const s = static.c_fastcall_sret();
6245 try expect(s.a == 1);
6246 try expect(s.b == 2);
6247 try expect(s.c == 3);
6248 }
6249 {
6250 const s = static.c_fastcall_no_sret();
6251 try expect(s.a == 1);
6252 try expect(s.b == 2);
6253 }
6254 {
6255 const s = static.c_fastcall_no_sret_f32_f32();
6256 try expect(s.a == 1);
6257 try expect(s.b == 2);
6258 }
6259 {
6260 const s = static.c_fastcall_no_sret_f64();
6261 try expect(s.a == 1);
6262 }
6263 {
6264 const s = static.c_fastcall_ret_f32();
6265 try expect(s == 1);
6266 }
6267 {
6268 const s = static.c_fastcall_ret_f64();
6269 try expect(s == 1);
6270 }
6271
6272 static.run_c_fastcall_tests();
61766273}
61776274
61786275test "x86 vectorcall calling convention" {
61796276 if (builtin.cpu.arch != .x86) return error.SkipZigTest;
6277 if (builtin.os.tag != .windows) return error.SkipZigTest;
6278 if (builtin.abi != .msvc) return error.SkipZigTest;
6279
61806280 const static = struct {
61816281 extern fn c_vectorcall_check(a: c_int, b: f32, c: f64, d: *anyopaque, e: f32, f: f64, g: f64, h: f32, i: f32, j: c_int) callconv(.{ .x86_vectorcall = .{} }) void;
61826282 export fn zig_vectorcall_check(a: c_int, b: f32, c: f64, d: *anyopaque, e: f32, f: f64, g: f64, h: f32, i: f32, j: c_int) callconv(.{ .x86_vectorcall = .{} }) void {