From 070276d8f16667233e9d3a450a4e8d6db9183958 Mon Sep 17 00:00:00 2001 From: Guy Fischman Date: Wed, 7 Oct 2026 20:52:12 +0200 Subject: [PATCH] compiler: add x86_64, aarch64, riscv, and loongarch preserve_most calling convention support --- lib/std/Target.zig | 6 +++++ lib/std/lang.zig | 6 +++++ src/Zcu.zig | 6 +++++ src/codegen/c/type.zig | 9 +++++++ src/codegen/llvm.zig | 6 +++++ src/link/Dwarf.zig | 7 +++++ src/link/Dwarf2.zig | 2 ++ test/c_abi/cfuncs.c | 10 +++++++ test/c_abi/main.zig | 27 +++++++++++++++++++ ..._preserve_most_on_unsupported_platform.zig | 16 +++++++++++ ..._preserve_none_on_unsupported_platform.zig | 2 +- 11 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 test/cases/compile_errors/callconv_preserve_most_on_unsupported_platform.zig diff --git a/lib/std/Target.zig b/lib/std/Target.zig index 51f11ad7642ed3db3023f2414832428fa73d7244..9900737e847c002383d8e4dd546b0a23066ebd5f 100644 --- a/lib/std/Target.zig +++ b/lib/std/Target.zig @@ -1850,6 +1850,7 @@ pub const Cpu = struct { .x86_64_vectorcall, .x86_64_interrupt, .x86_64_preserve_none, + .x86_64_preserve_most, => &.{.x86_64}, .x86_sysv, @@ -1877,6 +1878,7 @@ pub const Cpu = struct { .aarch64_vfabi, .aarch64_vfabi_sve, .aarch64_preserve_none, + .aarch64_preserve_most, => &.{ .aarch64, .aarch64_be }, .alpha_osf, @@ -1899,11 +1901,13 @@ pub const Cpu = struct { .riscv64_lp64, .riscv64_lp64_v, .riscv64_interrupt, + .riscv64_preserve_most, => &.{ .riscv64, .riscv64be }, .riscv32_ilp32, .riscv32_ilp32_v, .riscv32_interrupt, + .riscv32_preserve_most, => &.{ .riscv32, .riscv32be }, .sparc64_sysv, @@ -1962,10 +1966,12 @@ pub const Cpu = struct { .loongarch64_lp64, .loongarch64_preserve_none, + .loongarch64_preserve_most, => &.{.loongarch64}, .loongarch32_ilp32, .loongarch32_preserve_none, + .loongarch32_preserve_most, => &.{.loongarch32}, .m68k_sysv, diff --git a/lib/std/lang.zig b/lib/std/lang.zig index ef98f3ce8f3d61778ec9c5fe7ead8234a6bb9b72..42c2f49e7f59476511811316fa17d09953bb9056 100644 --- a/lib/std/lang.zig +++ b/lib/std/lang.zig @@ -209,6 +209,7 @@ pub const CallingConvention = union(enum(u8)) { x86_64_vectorcall: CommonOptions, x86_64_interrupt: CommonOptions, x86_64_preserve_none: CommonOptions, + x86_64_preserve_most: CommonOptions, // Calling conventions for the `x86` architecture. x86_sysv: X86RegparmOptions, @@ -237,6 +238,7 @@ pub const CallingConvention = union(enum(u8)) { aarch64_vfabi: CommonOptions, aarch64_vfabi_sve: CommonOptions, aarch64_preserve_none: CommonOptions, + aarch64_preserve_most: CommonOptions, /// The standard `alpha` calling convention. alpha_osf: CommonOptions, @@ -261,11 +263,13 @@ pub const CallingConvention = union(enum(u8)) { riscv64_lp64: CommonOptions, riscv64_lp64_v: CommonOptions, riscv64_interrupt: RiscvInterruptOptions, + riscv64_preserve_most: CommonOptions, // Calling conventions for the `riscv32` architecture. riscv32_ilp32: CommonOptions, riscv32_ilp32_v: CommonOptions, riscv32_interrupt: RiscvInterruptOptions, + riscv32_preserve_most: CommonOptions, // Calling conventions for the `sparc64` architecture. sparc64_sysv: CommonOptions, @@ -323,10 +327,12 @@ pub const CallingConvention = union(enum(u8)) { /// The standard `loongarch64` calling convention. loongarch64_lp64: CommonOptions, loongarch64_preserve_none: CommonOptions, + loongarch64_preserve_most: CommonOptions, /// The standard `loongarch32` calling convention. loongarch32_ilp32: CommonOptions, loongarch32_preserve_none: CommonOptions, + loongarch32_preserve_most: CommonOptions, // Calling conventions for the `m68k` architecture. m68k_sysv: CommonOptions, diff --git a/src/Zcu.zig b/src/Zcu.zig index c9bf50b2b7f14400c0b524915b23da0b93ecc961..01b136b01514721616febf43f65827b36d0ced4f 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -4683,6 +4683,7 @@ pub fn callconvSupported(zcu: *Zcu, cc: std.lang.CallingConvention) union(enum) .x86_64_regcall_v4_win, .x86_64_interrupt, .x86_64_preserve_none, + .x86_64_preserve_most, .x86_fastcall, .x86_thiscall, .x86_vectorcall, @@ -4692,6 +4693,7 @@ pub fn callconvSupported(zcu: *Zcu, cc: std.lang.CallingConvention) union(enum) .aarch64_vfabi, .aarch64_vfabi_sve, .aarch64_preserve_none, + .aarch64_preserve_most, .arm_aapcs, .csky_interrupt, .riscv64_lp64_v, @@ -4713,6 +4715,10 @@ pub fn callconvSupported(zcu: *Zcu, cc: std.lang.CallingConvention) union(enum) .ez80_tiflags, .loongarch32_preserve_none, .loongarch64_preserve_none, + .riscv32_preserve_most, + .riscv64_preserve_most, + .loongarch32_preserve_most, + .loongarch64_preserve_most, .naked, => true, // incoming stack alignment supported diff --git a/src/codegen/c/type.zig b/src/codegen/c/type.zig index c77467de9c8534dbbeabb9bd01a7e72db310c90c..c05b29ee8977c29372f28ecece237b3b3bd5c912 100644 --- a/src/codegen/c/type.zig +++ b/src/codegen/c/type.zig @@ -63,6 +63,7 @@ pub const CType = union(enum) { regcall, preserve_none, + preserve_most, aarch64_vector_pcs, aarch64_sve_pcs, @@ -146,6 +147,14 @@ pub const CType = union(enum) { .loongarch64_preserve_none, => .preserve_none, + .x86_64_preserve_most, + .aarch64_preserve_most, + .riscv32_preserve_most, + .riscv64_preserve_most, + .loongarch32_preserve_most, + .loongarch64_preserve_most, + => .preserve_most, + .aarch64_vfabi => .aarch64_vector_pcs, .aarch64_vfabi_sve => .aarch64_sve_pcs, diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 4b848ca38d7196f7fc204d109dbcbf576f25d2a7..68801420297030ba6ecd76a69c2146086f92cb5b 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -4304,6 +4304,7 @@ pub fn toLlvmCallConvTag(cc_tag: std.lang.CallingConvention.Tag, target: *const .x86_64_vectorcall => .x86_vectorcallcc, .x86_64_interrupt => .x86_intrcc, .x86_64_preserve_none => .preserve_nonecc, + .x86_64_preserve_most => .preserve_mostcc, .x86_stdcall => .x86_stdcallcc, .x86_fastcall => .x86_fastcallcc, .x86_thiscall => .x86_thiscallcc, @@ -4320,6 +4321,7 @@ pub fn toLlvmCallConvTag(cc_tag: std.lang.CallingConvention.Tag, target: *const .aarch64_vfabi => .aarch64_vector_pcs, .aarch64_vfabi_sve => .aarch64_sve_vector_pcs, .aarch64_preserve_none => .preserve_nonecc, + .aarch64_preserve_most => .preserve_mostcc, .arm_aapcs => .arm_aapcscc, .arm_aapcs_vfp => .arm_aapcs_vfpcc, .riscv64_lp64_v => .riscv_vectorcallcc, @@ -4336,6 +4338,10 @@ pub fn toLlvmCallConvTag(cc_tag: std.lang.CallingConvention.Tag, target: *const .nvptx_kernel => .ptx_kernel, .loongarch32_preserve_none => .preserve_nonecc, .loongarch64_preserve_none => .preserve_nonecc, + .riscv32_preserve_most => .preserve_mostcc, + .riscv64_preserve_most => .preserve_mostcc, + .loongarch32_preserve_most => .preserve_mostcc, + .loongarch64_preserve_most => .preserve_mostcc, // Calling conventions which LLVM uses function attributes for. .riscv64_interrupt, diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index fab66811a051ae024a309bc1d210025017054844..a9b309e1d71e2c03e71e7e1addf5055a74d965d2 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -4167,6 +4167,7 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co .x86_64_vectorcall => .LLVM_vectorcall, .x86_sysv, .x86_win, .x86_mingw => .normal, .x86_64_preserve_none => .LLVM_PreserveNone, + .x86_64_preserve_most => .LLVM_PreserveMost, .x86_stdcall => .BORLAND_stdcall, .x86_fastcall => .BORLAND_msfastcall, .x86_thiscall => .BORLAND_thiscall, @@ -4181,6 +4182,7 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co .aarch64_vfabi => .LLVM_AAPCS, .aarch64_vfabi_sve => .LLVM_AAPCS, .aarch64_preserve_none => .LLVM_PreserveNone, + .aarch64_preserve_most => .LLVM_PreserveMost, .arm_aapcs => .LLVM_AAPCS, .arm_aapcs_vfp => .LLVM_AAPCS_VFP, @@ -4192,6 +4194,11 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co .loongarch32_preserve_none => .LLVM_PreserveNone, .loongarch64_preserve_none => .LLVM_PreserveNone, + .riscv32_preserve_most => .LLVM_PreserveMost, + .riscv64_preserve_most => .LLVM_PreserveMost, + .loongarch32_preserve_most => .LLVM_PreserveMost, + .loongarch64_preserve_most => .LLVM_PreserveMost, + .m68k_rtd => .LLVM_M68kRTD, .sh_renesas => .GNU_renesas_sh, diff --git a/src/link/Dwarf2.zig b/src/link/Dwarf2.zig index 8c61805ae6ca85c1514ef0045934a8b7022840bd..28887a5318395cb05b8ce64bfd8f932dfd359a1d 100644 --- a/src/link/Dwarf2.zig +++ b/src/link/Dwarf2.zig @@ -3276,6 +3276,7 @@ fn updateConstInner( .x86_64_vectorcall => .LLVM_vectorcall, .x86_sysv, .x86_win, .x86_mingw => .normal, .x86_64_preserve_none => .LLVM_PreserveNone, + .x86_64_preserve_most => .LLVM_PreserveMost, .x86_stdcall => .BORLAND_stdcall, .x86_fastcall => .BORLAND_msfastcall, .x86_thiscall => .BORLAND_thiscall, @@ -3290,6 +3291,7 @@ fn updateConstInner( .aarch64_vfabi => .LLVM_AAPCS, .aarch64_vfabi_sve => .LLVM_AAPCS, .aarch64_preserve_none => .LLVM_PreserveNone, + .aarch64_preserve_most => .LLVM_PreserveMost, .arm_aapcs => .LLVM_AAPCS, .arm_aapcs_vfp => .LLVM_AAPCS_VFP, diff --git a/test/c_abi/cfuncs.c b/test/c_abi/cfuncs.c index ee29cf5738129307d7b359a33cd83a486879f754..6488e4a737bdc13fac101a454c0b5720416a853a 100644 --- a/test/c_abi/cfuncs.c +++ b/test/c_abi/cfuncs.c @@ -16700,6 +16700,16 @@ void c_preserve_none_check(void) { } #endif +#if defined __x86_64__ || defined __aarch64__ || defined __riscv || defined __loongarch__ +int __attribute__((preserve_most)) c_preserve_most(int x) { + return x + 1; +} +int __attribute__((preserve_most)) zig_preserve_most(int); +void c_preserve_most_check(void) { + assert_or_panic(zig_preserve_most(41) == 42); +} +#endif + struct byval_tail_callsite_attr_Point { double x; double y; diff --git a/test/c_abi/main.zig b/test/c_abi/main.zig index cb8ed58ea696dfa3a5a886aae1256016e76688c2..2dee44a4501504302f53b7c5b16af0926fc9b742 100644 --- a/test/c_abi/main.zig +++ b/test/c_abi/main.zig @@ -18162,3 +18162,30 @@ test "preserve_none calling convention" { try expect(static.c_preserve_none(41) == 42); static.c_preserve_none_check(); } + +const preserve_most_cc: ?std.lang.CallingConvention = if (builtin.zig_backend != .stage2_llvm) + null +else switch (builtin.cpu.arch) { + .x86_64 => .{ .x86_64_preserve_most = .{} }, + .aarch64, .aarch64_be => .{ .aarch64_preserve_most = .{} }, + // Supported in LLVM but not yet wired up in Clang. + // .riscv32, .riscv32be => .{ .riscv32_preserve_most = .{} }, + // .riscv64, .riscv64be => .{ .riscv64_preserve_most = .{} }, + // .loongarch32 => .{ .loongarch32_preserve_most = .{} }, + // .loongarch64 => .{ .loongarch64_preserve_most = .{} }, + else => null, +}; + +export fn zig_preserve_most(x: i32) callconv(preserve_most_cc orelse .c) i32 { + return x + 1; +} + +test "preserve_most calling convention" { + if (preserve_most_cc == null) return error.SkipZigTest; + const static = struct { + extern fn c_preserve_most(x: i32) callconv(preserve_most_cc.?) i32; + extern fn c_preserve_most_check() void; + }; + try expect(static.c_preserve_most(41) == 42); + static.c_preserve_most_check(); +} diff --git a/test/cases/compile_errors/callconv_preserve_most_on_unsupported_platform.zig b/test/cases/compile_errors/callconv_preserve_most_on_unsupported_platform.zig new file mode 100644 index 0000000000000000000000000000000000000000..14810a3d62cb7b8aad837923935afb9e9a65609b --- /dev/null +++ b/test/cases/compile_errors/callconv_preserve_most_on_unsupported_platform.zig @@ -0,0 +1,16 @@ +const F1 = fn () callconv(.{ .x86_64_preserve_most = .{} }) void; +const F2 = fn () callconv(.{ .aarch64_preserve_most = .{} }) void; +export fn entry1() void { + const a: F1 = undefined; + _ = a; +} +export fn entry2() void { + const a: F2 = undefined; + _ = a; +} + +// error +// target=wasm32-freestanding +// +// :1:28: error: calling convention 'x86_64_preserve_most' only available on architectures 'x86_64' +// :2:28: error: calling convention 'aarch64_preserve_most' only available on architectures 'aarch64', 'aarch64_be' diff --git a/test/cases/compile_errors/callconv_preserve_none_on_unsupported_platform.zig b/test/cases/compile_errors/callconv_preserve_none_on_unsupported_platform.zig index 3997609595403f793e1aae1ac0f5c935e320a5c7..7c84e120d214df690eaa277cf6d03bcc0fadcc8c 100644 --- a/test/cases/compile_errors/callconv_preserve_none_on_unsupported_platform.zig +++ b/test/cases/compile_errors/callconv_preserve_none_on_unsupported_platform.zig @@ -10,7 +10,7 @@ export fn entry2() void { } // error -// target=riscv64-linux-none +// target=wasm32-freestanding // // :1:28: error: calling convention 'x86_64_preserve_none' only available on architectures 'x86_64' // :2:28: error: calling convention 'aarch64_preserve_none' only available on architectures 'aarch64', 'aarch64_be' -- 2.54.0