| author | |
| committer | |
| log | b95b61e4186adbf288a6dabb86b1f5450f45d3ab |
| tree | a161723e00d908df468562f976c809992cbf6408 |
| parent | 23f46bcd4d1d6cca5a25ed9e7b1222b1283de7af |
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 |
| 6944 | 6944 | .x86_64_win => x86_64_abi.classifyWindows(return_type, zcu, target, .ret) == .memory, |
| 6945 | 6945 | .x86_sysv, .x86_win => isByRef(return_type, zcu), |
| 6946 | 6946 | .x86_stdcall => !isScalar(zcu, return_type), |
| 6947 | .x86_fastcall => firstParamSRetX86Fastcall(zcu, return_type), | |
| 6947 | 6948 | .wasm_mvp => wasm_c_abi.classifyType(return_type, zcu) == .indirect, |
| 6948 | 6949 | .aarch64_aapcs, |
| 6949 | 6950 | .aarch64_aapcs_darwin, |
| ... | ... | @@ -6963,6 +6964,20 @@ pub fn firstParamSRet(fn_info: InternPool.Key.FuncType, zcu: *Zcu, target: *cons |
| 6963 | 6964 | }; |
| 6964 | 6965 | } |
| 6965 | 6966 | |
| 6967 | fn 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 | ||
| 6966 | 6981 | fn firstParamSRetSystemV(ty: Type, zcu: *Zcu, target: *const std.Target) bool { |
| 6967 | 6982 | const class = x86_64_abi.classifySystemV(ty, zcu, target, .ret); |
| 6968 | 6983 | if (class[0] == .memory) return true; |
| ... | ... | @@ -6984,10 +6999,10 @@ pub fn lowerFnRetTy(o: *Object, fn_info: InternPool.Key.FuncType) Allocator.Erro |
| 6984 | 6999 | switch (fn_info.cc) { |
| 6985 | 7000 | .@"inline" => unreachable, |
| 6986 | 7001 | .auto => return if (returnTypeByRef(zcu, target, return_type)) .void else o.lowerType(return_type), |
| 6987 | ||
| 6988 | 7002 | .x86_64_sysv => return lowerSystemVFnRetTy(o, fn_info), |
| 6989 | 7003 | .x86_64_win => return lowerWin64FnRetTy(o, fn_info), |
| 6990 | 7004 | .x86_stdcall => return if (isScalar(zcu, return_type)) o.lowerType(return_type) else .void, |
| 7005 | .x86_fastcall => return lowerX86FastcallFnRetTy(o, zcu, return_type), | |
| 6991 | 7006 | .x86_sysv, .x86_win => return if (isByRef(return_type, zcu)) .void else o.lowerType(return_type), |
| 6992 | 7007 | .aarch64_aapcs, .aarch64_aapcs_darwin, .aarch64_aapcs_win => switch (aarch64_c_abi.classifyType(return_type, zcu)) { |
| 6993 | 7008 | .memory => return .void, |
| ... | ... | @@ -7038,6 +7053,20 @@ pub fn lowerFnRetTy(o: *Object, fn_info: InternPool.Key.FuncType) Allocator.Erro |
| 7038 | 7053 | } |
| 7039 | 7054 | } |
| 7040 | 7055 | |
| 7056 | fn 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 | ||
| 7041 | 7070 | fn lowerWin64FnRetTy(o: *Object, fn_info: InternPool.Key.FuncType) Allocator.Error!Builder.Type { |
| 7042 | 7071 | const zcu = o.zcu; |
| 7043 | 7072 | 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) { |
| 153 | 153 | #define ZIG_NO_F128 |
| 154 | 154 | #endif |
| 155 | 155 | |
| 156 | #ifdef _MSC_VER | |
| 157 | #define ZIG_NO_F128 | |
| 158 | #endif | |
| 159 | ||
| 156 | 160 | #ifndef ZIG_NO_I128 |
| 157 | 161 | struct i128 { |
| 158 | 162 | __int128 value; |
| ... | ... | @@ -198,12 +202,14 @@ void zig_ptr(void *); |
| 198 | 202 | |
| 199 | 203 | void zig_bool(bool); |
| 200 | 204 | |
| 205 | #ifndef ZIG_NO_COMPLEX | |
| 201 | 206 | // Note: These two functions match the signature of __mulsc3 and __muldc3 in compiler-rt (and libgcc) |
| 202 | 207 | float complex zig_cmultf_comp(float a_r, float a_i, float b_r, float b_i); |
| 203 | 208 | double complex zig_cmultd_comp(double a_r, double a_i, double b_r, double b_i); |
| 204 | 209 | |
| 205 | 210 | float complex zig_cmultf(float complex a, float complex b); |
| 206 | 211 | double complex zig_cmultd(double complex a, double complex b); |
| 212 | #endif | |
| 207 | 213 | |
| 208 | 214 | struct Struct_u8 { |
| 209 | 215 | uint8_t a; |
| ... | ... | @@ -5312,6 +5318,7 @@ void c_five_floats(float a, float b, float c, float d, float e) { |
| 5312 | 5318 | assert_or_panic(e == 5.0); |
| 5313 | 5319 | } |
| 5314 | 5320 | |
| 5321 | #ifndef ZIG_NO_COMPLEX | |
| 5315 | 5322 | float complex c_cmultf_comp(float a_r, float a_i, float b_r, float b_i) { |
| 5316 | 5323 | assert_or_panic(a_r == 1.25f); |
| 5317 | 5324 | assert_or_panic(a_i == 2.6f); |
| ... | ... | @@ -5347,6 +5354,7 @@ double complex c_cmultd(double complex a, double complex b) { |
| 5347 | 5354 | |
| 5348 | 5355 | return 1.5 + I * 13.5; |
| 5349 | 5356 | } |
| 5357 | #endif | |
| 5350 | 5358 | |
| 5351 | 5359 | struct Struct_i32_i32 c_mut_struct_i32_i32(struct Struct_i32_i32 s) { |
| 5352 | 5360 | assert_or_panic(s.a == 1); |
| ... | ... | @@ -5792,7 +5800,7 @@ f16_struct c_f16_struct(f16_struct a) { |
| 5792 | 5800 | return (f16_struct){34}; |
| 5793 | 5801 | } |
| 5794 | 5802 | |
| 5795 | #if defined __x86_64__ || defined __i386__ | |
| 5803 | #if (defined __x86_64__ || defined __i386__) && !defined _MSC_VER | |
| 5796 | 5804 | typedef long double f80; |
| 5797 | 5805 | f80 c_f80(f80 a) { |
| 5798 | 5806 | assert_or_panic((double)a == 12.34); |
| ... | ... | @@ -5907,7 +5915,7 @@ double c_byval_tail_callsite_attr(struct byval_tail_callsite_attr_Rect in) { |
| 5907 | 5915 | return in.size.width; |
| 5908 | 5916 | } |
| 5909 | 5917 | |
| 5910 | #ifdef __i386__ | |
| 5918 | #if defined(__i386__) && defined(_WIN32) && !defined(_WIN64) && defined(_MSC_VER) | |
| 5911 | 5919 | void __attribute__((fastcall)) zig_fastcall_check(int a, float b, void *c, double d, int e); |
| 5912 | 5920 | void __attribute__((fastcall)) c_fastcall_check(int a, float b, void *c, double d, int e) { |
| 5913 | 5921 | assert_or_panic(a == 1); |
| ... | ... | @@ -5915,7 +5923,98 @@ void __attribute__((fastcall)) c_fastcall_check(int a, float b, void *c, double |
| 5915 | 5923 | assert_or_panic((uintptr_t)c == 3); |
| 5916 | 5924 | assert_or_panic(d == 4.0); |
| 5917 | 5925 | assert_or_panic(e == 5); |
| 5918 | zig_fastcall_check(a, b, c, d, e); | |
| 5926 | } | |
| 5927 | ||
| 5928 | typedef struct { | |
| 5929 | int a; | |
| 5930 | int b; | |
| 5931 | int c; | |
| 5932 | } FastcallSRet; | |
| 5933 | FastcallSRet __attribute__((fastcall)) zig_fastcall_sret(void); | |
| 5934 | FastcallSRet __attribute__((fastcall)) c_fastcall_sret(void) { | |
| 5935 | return (FastcallSRet){ | |
| 5936 | .a = 1, | |
| 5937 | .b = 2, | |
| 5938 | .c = 3 | |
| 5939 | }; | |
| 5940 | } | |
| 5941 | ||
| 5942 | typedef struct { | |
| 5943 | char a; | |
| 5944 | short b; | |
| 5945 | } FastcallNoSRet; | |
| 5946 | FastcallNoSRet __attribute__((fastcall)) zig_fastcall_no_sret(void); | |
| 5947 | FastcallNoSRet __attribute__((fastcall)) c_fastcall_no_sret(void) { | |
| 5948 | return (FastcallNoSRet){ | |
| 5949 | .a = 1, | |
| 5950 | .b = 2 | |
| 5951 | }; | |
| 5952 | } | |
| 5953 | ||
| 5954 | typedef struct { | |
| 5955 | float a; | |
| 5956 | float b; | |
| 5957 | } FastcallNoSRetF32F32; | |
| 5958 | FastcallNoSRetF32F32 __attribute__((fastcall)) zig_fastcall_no_sret_f32_f32(void); | |
| 5959 | FastcallNoSRetF32F32 __attribute__((fastcall)) c_fastcall_no_sret_f32_f32(void) { | |
| 5960 | return (FastcallNoSRetF32F32){ | |
| 5961 | .a = 1, | |
| 5962 | .b = 2 | |
| 5963 | }; | |
| 5964 | } | |
| 5965 | ||
| 5966 | typedef struct { | |
| 5967 | double a; | |
| 5968 | } FastcallNoSRetF64; | |
| 5969 | FastcallNoSRetF64 __attribute__((fastcall)) zig_fastcall_no_sret_f64(void); | |
| 5970 | FastcallNoSRetF64 __attribute__((fastcall)) c_fastcall_no_sret_f64(void) { | |
| 5971 | return (FastcallNoSRetF64){ | |
| 5972 | .a = 1 | |
| 5973 | }; | |
| 5974 | } | |
| 5975 | ||
| 5976 | float __attribute__((fastcall)) zig_fastcall_ret_f32(void); | |
| 5977 | float __attribute__((fastcall)) c_fastcall_ret_f32(void) { | |
| 5978 | return 1; | |
| 5979 | } | |
| 5980 | ||
| 5981 | double __attribute__((fastcall)) zig_fastcall_ret_f64(void); | |
| 5982 | double __attribute__((fastcall)) c_fastcall_ret_f64(void) { | |
| 5983 | return 1; | |
| 5984 | } | |
| 5985 | ||
| 5986 | void 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 | } | |
| 5919 | 6018 | } |
| 5920 | 6019 | |
| 5921 | 6020 | void __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 |
| 15 | 15 | builtin.cpu.arch != .hexagon and |
| 16 | 16 | builtin.cpu.arch != .s390x; // https://github.com/llvm/llvm-project/issues/168460 |
| 17 | 17 | |
| 18 | const have_f128 = builtin.cpu.arch.isWasm() or (builtin.cpu.arch.isX86() and !builtin.os.tag.isDarwin()); | |
| 19 | const have_f80 = builtin.cpu.arch.isX86(); | |
| 18 | const have_f128 = builtin.cpu.arch.isWasm() or (builtin.cpu.arch.isX86() and !builtin.os.tag.isDarwin() and builtin.abi != .msvc); | |
| 19 | const have_f80 = builtin.cpu.arch.isX86() and builtin.abi != .msvc; | |
| 20 | 20 | |
| 21 | 21 | extern fn run_c_tests() void; |
| 22 | 22 | |
| ... | ... | @@ -6162,21 +6162,121 @@ test "byval tail callsite attribute" { |
| 6162 | 6162 | |
| 6163 | 6163 | test "x86 fastcall calling convention" { |
| 6164 | 6164 | 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 | ||
| 6165 | 6168 | 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 { | |
| 6168 | 6173 | if (a != 1) @panic("test failure"); |
| 6169 | 6174 | if (b != 2.0) @panic("test failure"); |
| 6170 | 6175 | if (@intFromPtr(c) != 3) @panic("test failure"); |
| 6171 | 6176 | if (d != 4.0) @panic("test failure"); |
| 6172 | 6177 | if (e != 5) @panic("test failure"); |
| 6173 | 6178 | } |
| 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; | |
| 6174 | 6239 | }; |
| 6240 | ||
| 6175 | 6241 | 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(); | |
| 6176 | 6273 | } |
| 6177 | 6274 | |
| 6178 | 6275 | test "x86 vectorcall calling convention" { |
| 6179 | 6276 | 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 | ||
| 6180 | 6280 | const static = struct { |
| 6181 | 6281 | 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; |
| 6182 | 6282 | 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 { |