Commit 51706af908
Changed files (28)
lib
compiler
aro_translate_c
src
arch
aarch64
arm
riscv64
sparc64
wasm
codegen
lib/compiler/aro_translate_c/ast.zig
@@ -550,12 +550,24 @@ pub const Payload = struct {
is_var_args: bool,
name: ?[]const u8,
linksection_string: ?[]const u8,
- explicit_callconv: ?std.builtin.CallingConvention,
+ explicit_callconv: ?CallingConvention,
params: []Param,
return_type: Node,
body: ?Node,
alignment: ?c_uint,
},
+
+ pub const CallingConvention = enum {
+ c,
+ x86_64_sysv,
+ x86_stdcall,
+ x86_fastcall,
+ x86_thiscall,
+ x86_vectorcall,
+ aarch64_vfabi,
+ arm_aapcs,
+ arm_aapcs_vfp,
+ };
};
pub const Param = struct {
@@ -2812,14 +2824,50 @@ fn renderFunc(c: *Context, node: Node) !NodeIndex {
const callconv_expr = if (payload.explicit_callconv) |some| blk: {
_ = try c.addToken(.keyword_callconv, "callconv");
_ = try c.addToken(.l_paren, "(");
- _ = try c.addToken(.period, ".");
- const res = try c.addNode(.{
- .tag = .enum_literal,
- .main_token = try c.addTokenFmt(.identifier, "{s}", .{@tagName(some)}),
- .data = undefined,
- });
+ const cc_node = switch (some) {
+ .c => cc_node: {
+ _ = try c.addToken(.period, ".");
+ break :cc_node try c.addNode(.{
+ .tag = .enum_literal,
+ .main_token = try c.addToken(.identifier, "c"),
+ .data = undefined,
+ });
+ },
+ .x86_64_sysv,
+ .x86_stdcall,
+ .x86_fastcall,
+ .x86_thiscall,
+ .x86_vectorcall,
+ .aarch64_vfabi,
+ .arm_aapcs,
+ .arm_aapcs_vfp,
+ => cc_node: {
+ // .{ .foo = .{} }
+ _ = try c.addToken(.period, ".");
+ const outer_lbrace = try c.addToken(.l_brace, "{");
+ _ = try c.addToken(.period, ".");
+ _ = try c.addToken(.identifier, @tagName(some));
+ _ = try c.addToken(.equal, "=");
+ _ = try c.addToken(.period, ".");
+ const inner_lbrace = try c.addToken(.l_brace, "{");
+ _ = try c.addToken(.r_brace, "}");
+ _ = try c.addToken(.r_brace, "}");
+ break :cc_node try c.addNode(.{
+ .tag = .struct_init_dot_two,
+ .main_token = outer_lbrace,
+ .data = .{
+ .lhs = try c.addNode(.{
+ .tag = .struct_init_dot_two,
+ .main_token = inner_lbrace,
+ .data = .{ .lhs = 0, .rhs = 0 },
+ }),
+ .rhs = 0,
+ },
+ });
+ },
+ };
_ = try c.addToken(.r_paren, ")");
- break :blk res;
+ break :blk cc_node;
} else 0;
const return_type_expr = try renderNode(c, payload.return_type);
lib/std/builtin.zig
@@ -210,6 +210,336 @@ pub const CallingConvention = enum(u8) {
Vertex,
};
+/// The calling convention of a function defines how arguments and return values are passed, as well
+/// as any other requirements which callers and callees must respect, such as register preservation
+/// and stack alignment.
+///
+/// This data structure is used by the Zig language code generation and
+/// therefore must be kept in sync with the compiler implementation.
+///
+/// TODO: this will be renamed `CallingConvention` after an initial zig1.wasm update.
+pub const NewCallingConvention = union(enum(u8)) {
+ pub const Tag = @typeInfo(NewCallingConvention).@"union".tag_type.?;
+
+ /// This is an alias for the default C calling convention for this target.
+ /// Functions marked as `extern` or `export` are given this calling convention by default.
+ pub const c = builtin.target.defaultCCallingConvention().?;
+
+ pub const winapi: NewCallingConvention = switch (builtin.target.arch) {
+ .x86_64 => .{ .x86_64_win = .{} },
+ .x86 => .{ .x86_stdcall = .{} },
+ .aarch64, .aarch64_be => .{ .aarch64_aapcs_win = .{} },
+ .arm, .armeb, .thumb, .thumbeb => .{ .arm_aapcs_vfp = .{} },
+ else => unreachable,
+ };
+
+ pub const kernel: NewCallingConvention = switch (builtin.target.cpu.arch) {
+ .amdgcn => .amdgcn_kernel,
+ .nvptx, .nvptx64 => .nvptx_kernel,
+ .spirv, .spirv32, .spirv64 => .spirv_kernel,
+ else => unreachable,
+ };
+
+ /// Deprecated; use `.auto`.
+ pub const Unspecified: NewCallingConvention = .auto;
+ /// Deprecated; use `.c`.
+ pub const C: NewCallingConvention = .c;
+ /// Deprecated; use `.naked`.
+ pub const Naked: NewCallingConvention = .naked;
+ /// Deprecated; use `.@"async"`.
+ pub const Async: NewCallingConvention = .@"async";
+ /// Deprecated; use `.@"inline"`.
+ pub const Inline: NewCallingConvention = .@"inline";
+ /// Deprecated; use `.x86_64_interrupt`, `.x86_interrupt`, or `.avr_interrupt`.
+ pub const Interrupt: NewCallingConvention = switch (builtin.target.cpu.arch) {
+ .x86_64 => .{ .x86_64_interrupt = .{} },
+ .x86 => .{ .x86_interrupt = .{} },
+ .avr => .avr_interrupt,
+ else => unreachable,
+ };
+ /// Deprecated; use `.avr_signal`.
+ pub const Signal: NewCallingConvention = .avr_signal;
+ /// Deprecated; use `.x86_stdcall`.
+ pub const Stdcall: NewCallingConvention = .{ .x86_stdcall = .{} };
+ /// Deprecated; use `.x86_fastcall`.
+ pub const Fastcall: NewCallingConvention = .{ .x86_fastcall = .{} };
+ /// Deprecated; use `.x86_64_vectorcall`, `.x86_vectorcall`, or `aarch64_vfabi`.
+ pub const Vectorcall: NewCallingConvention = switch (builtin.target.cpu.arch) {
+ .x86_64 => .{ .x86_64_vectorcall = .{} },
+ .x86 => .{ .x86_vectorcall = .{} },
+ .aarch64, .aarch64_be => .{ .aarch64_vfabi = .{} },
+ else => unreachable,
+ };
+ /// Deprecated; use `.x86_thiscall`.
+ pub const Thiscall: NewCallingConvention = .{ .x86_thiscall = .{} };
+ /// Deprecated; use `.arm_apcs`.
+ pub const APCS: NewCallingConvention = .{ .arm_apcs = .{} };
+ /// Deprecated; use `.arm_aapcs`.
+ pub const AAPCS: NewCallingConvention = .{ .arm_aapcs = .{} };
+ /// Deprecated; use `.arm_aapcs_vfp`.
+ pub const AAPCSVFP: NewCallingConvention = .{ .arm_aapcs_vfp = .{} };
+ /// Deprecated; use `.x86_64_sysv`.
+ pub const SysV: NewCallingConvention = .{ .x86_64_sysv = .{} };
+ /// Deprecated; use `.x86_64_win`.
+ pub const Win64: NewCallingConvention = .{ .x86_64_win = .{} };
+ /// Deprecated; use `.kernel`.
+ pub const Kernel: NewCallingConvention = .kernel;
+ /// Deprecated; use `.spirv_fragment`.
+ pub const Fragment: NewCallingConvention = .spirv_fragment;
+ /// Deprecated; use `.spirv_vertex`.
+ pub const Vertex: NewCallingConvention = .spirv_vertex;
+
+ /// The default Zig calling convention when neither `export` nor `inline` is specified.
+ /// This calling convention makes no guarantees about stack alignment, registers, etc.
+ /// It can only be used within this Zig compilation unit.
+ auto,
+
+ /// The calling convention of a function that can be called with `async` syntax. An `async` call
+ /// of a runtime-known function must target a function with this calling convention.
+ /// Comptime-known functions with other calling conventions may be coerced to this one.
+ @"async",
+
+ /// Functions with this calling convention have no prologue or epilogue, making the function
+ /// uncallable in regular Zig code. This can be useful when integrating with assembly.
+ naked,
+
+ /// This calling convention is exactly equivalent to using the `inline` keyword on a function
+ /// definition. This function will be semantically inlined by the Zig compiler at call sites.
+ /// Pointers to inline functions are comptime-only.
+ @"inline",
+
+ // Calling conventions for the x86_64 architecture.
+ x86_64_sysv: CommonOptions,
+ x86_64_win: CommonOptions,
+ x86_64_regcall_v3_sysv: CommonOptions,
+ x86_64_regcall_v4_win: CommonOptions,
+ x86_64_vectorcall: CommonOptions,
+ x86_64_interrupt: CommonOptions,
+
+ // Calling conventions for the x86 architecture.
+ x86_sysv: X86RegparmOptions,
+ x86_win: X86RegparmOptions,
+ x86_stdcall: X86RegparmOptions,
+ x86_fastcall: CommonOptions,
+ x86_thiscall: CommonOptions,
+ x86_thiscall_mingw: CommonOptions,
+ x86_regcall_v3: CommonOptions,
+ x86_regcall_v4_win: CommonOptions,
+ x86_vectorcall: CommonOptions,
+ x86_interrupt: CommonOptions,
+
+ // Calling conventions for the aarch64 architecture.
+ aarch64_aapcs: CommonOptions,
+ aarch64_aapcs_darwin: CommonOptions,
+ aarch64_aapcs_win: CommonOptions,
+ aarch64_vfabi: CommonOptions,
+ aarch64_vfabi_sve: CommonOptions,
+
+ // Calling convetions for the arm architecture.
+ /// ARM Procedure Call Standard (obsolete)
+ arm_apcs: CommonOptions,
+ /// ARM Architecture Procedure Call Standard
+ arm_aapcs: CommonOptions,
+ /// ARM Architecture Procedure Call Standard Vector Floating-Point
+ arm_aapcs_vfp: CommonOptions,
+ arm_aapcs16_vfp: CommonOptions,
+ arm_interrupt: ArmInterruptOptions,
+
+ // Calling conventions for the mips64 architecture.
+ mips64_n64: CommonOptions,
+ mips64_n32: CommonOptions,
+ mips64_interrupt: MipsInterruptOptions,
+
+ // Calling conventions for the mips architecture.
+ mips_o32: CommonOptions,
+ mips_interrupt: MipsInterruptOptions,
+
+ // Calling conventions for the riscv64 architecture.
+ riscv64_lp64: CommonOptions,
+ riscv64_lp64_v: CommonOptions,
+ riscv64_interrupt: RiscvInterruptOptions,
+
+ // Calling conventions for the riscv32 architecture.
+ riscv32_ilp32: CommonOptions,
+ riscv32_ilp32_v: CommonOptions,
+ riscv32_interrupt: RiscvInterruptOptions,
+
+ // Calling conventions for the sparc64 architecture.
+ sparc64_sysv: CommonOptions,
+
+ // Calling conventions for the sparc architecture.
+ sparc_sysv: CommonOptions,
+
+ // Calling conventions for the powerpc64 architecture.
+ powerpc64_elf: CommonOptions,
+ powerpc64_elf_altivec: CommonOptions,
+ powerpc64_elf_v2: CommonOptions,
+
+ // Calling conventions for the powerpc architecture.
+ powerpc_sysv: CommonOptions,
+ powerpc_sysv_altivec: CommonOptions,
+ powerpc_aix: CommonOptions,
+ powerpc_aix_altivec: CommonOptions,
+
+ /// The standard wasm32/wasm64 calling convention, as specified in the WebAssembly Tool Conventions.
+ wasm_watc: CommonOptions,
+
+ /// The standard ARC calling convention.
+ arc_sysv: CommonOptions,
+
+ // Calling conventions for the AVR architecture.
+ avr_gnu,
+ avr_builtin,
+ avr_signal,
+ avr_interrupt,
+
+ /// The standard bpf calling convention.
+ bpf_std: CommonOptions,
+
+ // Calling conventions for the csky architecture.
+ csky_sysv: CommonOptions,
+ csky_interrupt: CommonOptions,
+
+ // Calling conventions for the hexagon architecture.
+ hexagon_sysv: CommonOptions,
+ hexagon_sysv_hvx: CommonOptions,
+
+ /// The standard Lanai calling convention.
+ lanai_sysv: CommonOptions,
+
+ /// The standard loongarch64 calling convention.
+ loongarch64_lp64: CommonOptions,
+
+ /// The standard loongarch32 calling convention.
+ loongarch32_ilp32: CommonOptions,
+
+ // Calling conventions for the m68k architecture.
+ m68k_sysv: CommonOptions,
+ m68k_gnu: CommonOptions,
+ m68k_rtd: CommonOptions,
+ m68k_interrupt: CommonOptions,
+
+ /// The standard MSP430 calling convention.
+ msp430_eabi: CommonOptions,
+
+ /// The standard propeller1 calling convention.
+ propeller1_sysv: CommonOptions,
+
+ /// The standard propeller1 calling convention.
+ propeller2_sysv: CommonOptions,
+
+ // Calling conventions for the S390X architecture.
+ s390x_sysv: CommonOptions,
+ s390x_sysv_vx: CommonOptions,
+
+ /// The standard VE calling convention.
+ ve_sysv: CommonOptions,
+
+ // Calling conventions for the xCORE architecture.
+ xcore_xs1: CommonOptions,
+ xcore_xs2: CommonOptions,
+
+ // Calling conventions for the Xtensa architecture.
+ xtensa_call0: CommonOptions,
+ xtensa_windowed: CommonOptions,
+
+ // Calling conventions for the AMDGCN architecture.
+ amdgcn_device: CommonOptions,
+ amdgcn_kernel,
+ amdgcn_cs: CommonOptions,
+
+ // Calling conventions for the NVPTX architecture.
+ nvptx_device,
+ nvptx_kernel,
+
+ // Calling conventions for SPIR-V kernels and shaders.
+ spirv_device,
+ spirv_kernel,
+ spirv_fragment,
+ spirv_vertex,
+
+ /// Options shared across most calling conventions.
+ pub const CommonOptions = struct {
+ /// The boundary the stack is aligned to when the function is called.
+ /// `null` means the default for this calling convention.
+ incoming_stack_alignment: ?u64 = null,
+ };
+
+ /// Options for x86 calling conventions which support the regparm attribute to pass some
+ /// arguments in registers.
+ pub const X86RegparmOptions = struct {
+ /// The boundary the stack is aligned to when the function is called.
+ /// `null` means the default for this calling convention.
+ incoming_stack_alignment: ?u64 = null,
+ /// The number of arguments to pass in registers before passing the remaining arguments
+ /// according to the calling convention.
+ /// Equivalent to `__attribute__((regparm(x)))` in Clang and GCC.
+ register_params: u2 = 0,
+ };
+
+ /// Options for the `arm_interrupt` calling convention.
+ pub const ArmInterruptOptions = struct {
+ /// The boundary the stack is aligned to when the function is called.
+ /// `null` means the default for this calling convention.
+ incoming_stack_alignment: ?u64 = null,
+ /// The kind of interrupt being received.
+ type: InterruptType = .generic,
+
+ pub const InterruptType = enum(u3) {
+ generic,
+ irq,
+ fiq,
+ swi,
+ abort,
+ undef,
+ };
+ };
+
+ /// Options for the `mips_interrupt` and `mips64_interrupt` calling conventions.
+ pub const MipsInterruptOptions = struct {
+ /// The boundary the stack is aligned to when the function is called.
+ /// `null` means the default for this calling convention.
+ incoming_stack_alignment: ?u64 = null,
+ /// The interrupt mode.
+ mode: InterruptMode = .eic,
+
+ pub const InterruptMode = enum(u4) {
+ eic,
+ sw0,
+ sw1,
+ hw0,
+ hw1,
+ hw2,
+ hw3,
+ hw4,
+ hw5,
+ };
+ };
+
+ /// Options for the `riscv32_interrupt` and `riscv64_interrupt` calling conventions.
+ pub const RiscvInterruptOptions = struct {
+ /// The boundary the stack is aligned to when the function is called.
+ /// `null` means the default for this calling convention.
+ incoming_stack_alignment: ?u64 = null,
+ /// The privilege level.
+ level: PrivilegeLevel = .machine,
+
+ pub const PrivilegeLevel = enum(u2) {
+ user,
+ supervisor,
+ machine,
+ };
+ };
+
+ /// Returns the array of `std.Target.Cpu.Arch` to which this `CallingConvention` applies.
+ /// Asserts that `cc` is not `.auto`, `.@"async"`, `.naked`, or `.@"inline"`.
+ pub const archs = std.Target.Cpu.Arch.fromCallconv;
+
+ pub fn eql(a: NewCallingConvention, b: NewCallingConvention) bool {
+ return std.meta.eql(a, b);
+ }
+};
+
/// This data structure is used by the Zig language code generation and
/// therefore must be kept in sync with the compiler implementation.
pub const AddressSpace = enum(u5) {
lib/std/Target.zig
@@ -1609,6 +1609,165 @@ pub const Cpu = struct {
else => ".X",
};
}
+
+ /// Returns the array of `Arch` to which a specific `std.builtin.CallingConvention` applies.
+ /// Asserts that `cc` is not `.auto`, `.@"async"`, `.naked`, or `.@"inline"`.
+ pub fn fromCallconv(cc: std.builtin.NewCallingConvention) []const Arch {
+ return switch (cc) {
+ .auto,
+ .@"async",
+ .naked,
+ .@"inline",
+ => unreachable,
+
+ .x86_64_sysv,
+ .x86_64_win,
+ .x86_64_regcall_v3_sysv,
+ .x86_64_regcall_v4_win,
+ .x86_64_vectorcall,
+ .x86_64_interrupt,
+ => &.{.x86_64},
+
+ .x86_sysv,
+ .x86_win,
+ .x86_stdcall,
+ .x86_fastcall,
+ .x86_thiscall,
+ .x86_thiscall_mingw,
+ .x86_regcall_v3,
+ .x86_regcall_v4_win,
+ .x86_vectorcall,
+ .x86_interrupt,
+ => &.{.x86},
+
+ .aarch64_aapcs,
+ .aarch64_aapcs_darwin,
+ .aarch64_aapcs_win,
+ .aarch64_vfabi,
+ .aarch64_vfabi_sve,
+ => &.{ .aarch64, .aarch64_be },
+
+ .arm_apcs,
+ .arm_aapcs,
+ .arm_aapcs_vfp,
+ .arm_aapcs16_vfp,
+ .arm_interrupt,
+ => &.{ .arm, .armeb, .thumb, .thumbeb },
+
+ .mips64_n64,
+ .mips64_n32,
+ .mips64_interrupt,
+ => &.{ .mips64, .mips64el },
+
+ .mips_o32,
+ .mips_interrupt,
+ => &.{ .mips, .mipsel },
+
+ .riscv64_lp64,
+ .riscv64_lp64_v,
+ .riscv64_interrupt,
+ => &.{.riscv64},
+
+ .riscv32_ilp32,
+ .riscv32_ilp32_v,
+ .riscv32_interrupt,
+ => &.{.riscv32},
+
+ .sparc64_sysv,
+ => &.{.sparc64},
+
+ .sparc_sysv,
+ => &.{.sparc},
+
+ .powerpc64_elf,
+ .powerpc64_elf_altivec,
+ .powerpc64_elf_v2,
+ => &.{ .powerpc64, .powerpc64le },
+
+ .powerpc_sysv,
+ .powerpc_sysv_altivec,
+ .powerpc_aix,
+ .powerpc_aix_altivec,
+ => &.{ .powerpc, .powerpcle },
+
+ .wasm_watc,
+ => &.{ .wasm64, .wasm32 },
+
+ .arc_sysv,
+ => &.{.arc},
+
+ .avr_gnu,
+ .avr_builtin,
+ .avr_signal,
+ .avr_interrupt,
+ => &.{.avr},
+
+ .bpf_std,
+ => &.{ .bpfel, .bpfeb },
+
+ .csky_sysv,
+ .csky_interrupt,
+ => &.{.csky},
+
+ .hexagon_sysv,
+ .hexagon_sysv_hvx,
+ => &.{.hexagon},
+
+ .lanai_sysv,
+ => &.{.lanai},
+
+ .loongarch64_lp64,
+ => &.{.loongarch64},
+
+ .loongarch32_ilp32,
+ => &.{.loongarch32},
+
+ .m68k_sysv,
+ .m68k_gnu,
+ .m68k_rtd,
+ .m68k_interrupt,
+ => &.{.m68k},
+
+ .msp430_eabi,
+ => &.{.msp430},
+
+ .propeller1_sysv,
+ => &.{.propeller1},
+
+ .propeller2_sysv,
+ => &.{.propeller2},
+
+ .s390x_sysv,
+ .s390x_sysv_vx,
+ => &.{.s390x},
+
+ .ve_sysv,
+ => &.{.ve},
+
+ .xcore_xs1,
+ .xcore_xs2,
+ => &.{.xcore},
+
+ .xtensa_call0,
+ .xtensa_windowed,
+ => &.{.xtensa},
+
+ .amdgcn_device,
+ .amdgcn_kernel,
+ .amdgcn_cs,
+ => &.{.amdgcn},
+
+ .nvptx_device,
+ .nvptx_kernel,
+ => &.{ .nvptx, .nvptx64 },
+
+ .spirv_device,
+ .spirv_kernel,
+ .spirv_fragment,
+ .spirv_vertex,
+ => &.{ .spirv, .spirv32, .spirv64 },
+ };
+ }
};
pub const Model = struct {
@@ -2873,6 +3032,70 @@ pub fn cTypePreferredAlignment(target: Target, c_type: CType) u16 {
);
}
+pub fn defaultCCallingConvention(target: Target) ?std.builtin.NewCallingConvention {
+ return switch (target.cpu.arch) {
+ .x86_64 => switch (target.os.tag) {
+ .windows, .uefi => .{ .x86_64_win = .{} },
+ else => .{ .x86_64_sysv = .{} },
+ },
+ .x86 => switch (target.os.tag) {
+ .windows, .uefi => .{ .x86_win = .{} },
+ else => .{ .x86_sysv = .{} },
+ },
+ .aarch64, .aarch64_be => if (target.os.tag.isDarwin()) cc: {
+ break :cc .{ .aarch64_aapcs_darwin = .{} };
+ } else switch (target.os.tag) {
+ .windows => .{ .aarch64_aapcs_win = .{} },
+ else => .{ .aarch64_aapcs = .{} },
+ },
+ .arm, .armeb, .thumb, .thumbeb => .{ .arm_aapcs = .{} },
+ .mips64, .mips64el => switch (target.abi) {
+ .gnuabin32 => .{ .mips64_n32 = .{} },
+ else => .{ .mips64_n64 = .{} },
+ },
+ .mips, .mipsel => .{ .mips_o32 = .{} },
+ .riscv64 => .{ .riscv64_lp64 = .{} },
+ .riscv32 => .{ .riscv32_ilp32 = .{} },
+ .sparc64 => .{ .sparc64_sysv = .{} },
+ .sparc => .{ .sparc_sysv = .{} },
+ .powerpc64 => if (target.isMusl())
+ .{ .powerpc64_elf_v2 = .{} }
+ else
+ .{ .powerpc64_elf = .{} },
+ .powerpc64le => .{ .powerpc64_elf_v2 = .{} },
+ .powerpc, .powerpcle => switch (target.os.tag) {
+ .aix => .{ .powerpc_aix = .{} },
+ else => .{ .powerpc_sysv = .{} },
+ },
+ .wasm32 => .{ .wasm_watc = .{} },
+ .wasm64 => .{ .wasm_watc = .{} },
+ .arc => .{ .arc_sysv = .{} },
+ .avr => .avr_gnu,
+ .bpfel, .bpfeb => .{ .bpf_std = .{} },
+ .csky => .{ .csky_sysv = .{} },
+ .hexagon => .{ .hexagon_sysv = .{} },
+ .kalimba => null,
+ .lanai => .{ .lanai_sysv = .{} },
+ .loongarch64 => .{ .loongarch64_lp64 = .{} },
+ .loongarch32 => .{ .loongarch32_ilp32 = .{} },
+ .m68k => if (target.abi.isGnu() or target.abi.isMusl())
+ .{ .m68k_gnu = .{} }
+ else
+ .{ .m68k_sysv = .{} },
+ .msp430 => .{ .msp430_eabi = .{} },
+ .propeller1 => .{ .propeller1_sysv = .{} },
+ .propeller2 => .{ .propeller2_sysv = .{} },
+ .s390x => .{ .s390x_sysv = .{} },
+ .spu_2 => null,
+ .ve => .{ .ve_sysv = .{} },
+ .xcore => .{ .xcore_xs1 = .{} },
+ .xtensa => .{ .xtensa_call0 = .{} },
+ .amdgcn => .{ .amdgcn_device = .{} },
+ .nvptx, .nvptx64 => .nvptx_device,
+ .spirv, .spirv32, .spirv64 => .spirv_device,
+ };
+}
+
pub fn osArchName(target: std.Target) [:0]const u8 {
return target.os.tag.archName(target.cpu.arch);
}
src/arch/aarch64/CodeGen.zig
@@ -468,7 +468,7 @@ fn gen(self: *Self) !void {
const pt = self.pt;
const zcu = pt.zcu;
const cc = self.fn_type.fnCallingConvention(zcu);
- if (cc != .Naked) {
+ if (cc != .naked) {
// stp fp, lr, [sp, #-16]!
_ = try self.addInst(.{
.tag = .stp,
@@ -6229,14 +6229,14 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
const ret_ty = fn_ty.fnReturnType(zcu);
switch (cc) {
- .Naked => {
+ .naked => {
assert(result.args.len == 0);
result.return_value = .{ .unreach = {} };
result.stack_byte_count = 0;
result.stack_align = 1;
return result;
},
- .C => {
+ .aarch64_aapcs, .aarch64_aapcs_darwin, .aarch64_aapcs_win => {
// ARM64 Procedure Call Standard
var ncrn: usize = 0; // Next Core Register Number
var nsaa: u32 = 0; // Next stacked argument address
@@ -6266,7 +6266,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
// We round up NCRN only for non-Apple platforms which allow the 16-byte aligned
// values to spread across odd-numbered registers.
- if (Type.fromInterned(ty).abiAlignment(zcu) == .@"16" and !self.target.isDarwin()) {
+ if (Type.fromInterned(ty).abiAlignment(zcu) == .@"16" and cc != .aarch64_aapcs_darwin) {
// Round up NCRN to the next even number
ncrn += ncrn % 2;
}
@@ -6298,7 +6298,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
result.stack_byte_count = nsaa;
result.stack_align = 16;
},
- .Unspecified => {
+ .auto => {
if (ret_ty.zigTypeTag(zcu) == .noreturn) {
result.return_value = .{ .unreach = {} };
} else if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu) and !ret_ty.isError(zcu)) {
src/arch/arm/CodeGen.zig
@@ -475,7 +475,7 @@ fn gen(self: *Self) !void {
const pt = self.pt;
const zcu = pt.zcu;
const cc = self.fn_type.fnCallingConvention(zcu);
- if (cc != .Naked) {
+ if (cc != .naked) {
// push {fp, lr}
const push_reloc = try self.addNop();
@@ -6196,14 +6196,14 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
const ret_ty = fn_ty.fnReturnType(zcu);
switch (cc) {
- .Naked => {
+ .naked => {
assert(result.args.len == 0);
result.return_value = .{ .unreach = {} };
result.stack_byte_count = 0;
result.stack_align = 1;
return result;
},
- .C => {
+ .arm_aapcs => {
// ARM Procedure Call Standard, Chapter 6.5
var ncrn: usize = 0; // Next Core Register Number
var nsaa: u32 = 0; // Next stacked argument address
@@ -6254,7 +6254,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
result.stack_byte_count = nsaa;
result.stack_align = 8;
},
- .Unspecified => {
+ .auto => {
if (ret_ty.zigTypeTag(zcu) == .noreturn) {
result.return_value = .{ .unreach = {} };
} else if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu) and !ret_ty.isError(zcu)) {
src/arch/riscv64/CodeGen.zig
@@ -977,7 +977,7 @@ pub fn generateLazy(
.pt = pt,
.allocator = gpa,
.mir = mir,
- .cc = .Unspecified,
+ .cc = .auto,
.src_loc = src_loc,
.output_mode = comp.config.output_mode,
.link_mode = comp.config.link_mode,
@@ -1036,7 +1036,7 @@ fn formatWipMir(
.instructions = data.func.mir_instructions.slice(),
.frame_locs = data.func.frame_locs.slice(),
},
- .cc = .Unspecified,
+ .cc = .auto,
.src_loc = data.func.src_loc,
.output_mode = comp.config.output_mode,
.link_mode = comp.config.link_mode,
@@ -1238,7 +1238,7 @@ fn gen(func: *Func) !void {
}
}
- if (fn_info.cc != .Naked) {
+ if (fn_info.cc != .naked) {
_ = try func.addPseudo(.pseudo_dbg_prologue_end);
const backpatch_stack_alloc = try func.addPseudo(.pseudo_dead);
@@ -4894,7 +4894,7 @@ fn genCall(
.lib => |lib| try pt.funcType(.{
.param_types = lib.param_types,
.return_type = lib.return_type,
- .cc = .C,
+ .cc = func.target.defaultCCallingConvention().?,
}),
};
@@ -8289,12 +8289,12 @@ fn resolveCallingConventionValues(
const ret_ty = Type.fromInterned(fn_info.return_type);
switch (cc) {
- .Naked => {
+ .naked => {
assert(result.args.len == 0);
result.return_value = InstTracking.init(.unreach);
result.stack_align = .@"8";
},
- .C, .Unspecified => {
+ .riscv64_lp64, .auto => {
if (result.args.len > 8) {
return func.fail("RISC-V calling convention does not support more than 8 arguments", .{});
}
@@ -8359,7 +8359,7 @@ fn resolveCallingConventionValues(
for (param_types, result.args) |ty, *arg| {
if (!ty.hasRuntimeBitsIgnoreComptime(zcu)) {
- assert(cc == .Unspecified);
+ assert(cc == .auto);
arg.* = .none;
continue;
}
src/arch/riscv64/Lower.zig
@@ -6,7 +6,7 @@ link_mode: std.builtin.LinkMode,
pic: bool,
allocator: Allocator,
mir: Mir,
-cc: std.builtin.CallingConvention,
+cc: std.builtin.NewCallingConvention,
err_msg: ?*ErrorMsg = null,
src_loc: Zcu.LazySrcLoc,
result_insts_len: u8 = undefined,
src/arch/sparc64/CodeGen.zig
@@ -366,7 +366,7 @@ fn gen(self: *Self) !void {
const pt = self.pt;
const zcu = pt.zcu;
const cc = self.fn_type.fnCallingConvention(zcu);
- if (cc != .Naked) {
+ if (cc != .naked) {
// TODO Finish function prologue and epilogue for sparc64.
// save %sp, stack_reserved_area, %sp
@@ -4441,14 +4441,14 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type, role: RegisterView)
const ret_ty = fn_ty.fnReturnType(zcu);
switch (cc) {
- .Naked => {
+ .naked => {
assert(result.args.len == 0);
result.return_value = .{ .unreach = {} };
result.stack_byte_count = 0;
result.stack_align = .@"1";
return result;
},
- .Unspecified, .C => {
+ .auto, .sparc64_sysv => {
// SPARC Compliance Definition 2.4.1, Chapter 3
// Low-Level System Information (64-bit psABI) - Function Calling Sequence
src/arch/wasm/CodeGen.zig
@@ -1145,7 +1145,7 @@ fn ensureAllocLocal(func: *CodeGen, ty: Type) InnerError!WValue {
/// Memory is owned by the caller.
fn genFunctype(
gpa: Allocator,
- cc: std.builtin.CallingConvention,
+ cc: std.builtin.NewCallingConvention,
params: []const InternPool.Index,
return_type: Type,
pt: Zcu.PerThread,
@@ -1160,7 +1160,7 @@ fn genFunctype(
if (firstParamSRet(cc, return_type, pt, target)) {
try temp_params.append(.i32); // memory address is always a 32-bit handle
} else if (return_type.hasRuntimeBitsIgnoreComptime(zcu)) {
- if (cc == .C) {
+ if (cc == .wasm_watc) {
const res_classes = abi.classifyType(return_type, zcu);
assert(res_classes[0] == .direct and res_classes[1] == .none);
const scalar_type = abi.scalarType(return_type, zcu);
@@ -1178,7 +1178,7 @@ fn genFunctype(
if (!param_type.hasRuntimeBitsIgnoreComptime(zcu)) continue;
switch (cc) {
- .C => {
+ .wasm_watc => {
const param_classes = abi.classifyType(param_type, zcu);
if (param_classes[1] == .none) {
if (param_classes[0] == .direct) {
@@ -1367,7 +1367,7 @@ fn resolveCallingConventionValues(func: *CodeGen, fn_ty: Type) InnerError!CallWV
.args = &.{},
.return_value = .none,
};
- if (cc == .Naked) return result;
+ if (cc == .naked) return result;
var args = std.ArrayList(WValue).init(func.gpa);
defer args.deinit();
@@ -1382,7 +1382,7 @@ fn resolveCallingConventionValues(func: *CodeGen, fn_ty: Type) InnerError!CallWV
}
switch (cc) {
- .Unspecified => {
+ .auto => {
for (fn_info.param_types.get(ip)) |ty| {
if (!Type.fromInterned(ty).hasRuntimeBitsIgnoreComptime(zcu)) {
continue;
@@ -1392,7 +1392,7 @@ fn resolveCallingConventionValues(func: *CodeGen, fn_ty: Type) InnerError!CallWV
func.local_index += 1;
}
},
- .C => {
+ .wasm_watc => {
for (fn_info.param_types.get(ip)) |ty| {
const ty_classes = abi.classifyType(Type.fromInterned(ty), zcu);
for (ty_classes) |class| {
@@ -1408,10 +1408,11 @@ fn resolveCallingConventionValues(func: *CodeGen, fn_ty: Type) InnerError!CallWV
return result;
}
-fn firstParamSRet(cc: std.builtin.CallingConvention, return_type: Type, pt: Zcu.PerThread, target: std.Target) bool {
+fn firstParamSRet(cc: std.builtin.NewCallingConvention, return_type: Type, pt: Zcu.PerThread, target: std.Target) bool {
switch (cc) {
- .Unspecified, .Inline => return isByRef(return_type, pt, target),
- .C => {
+ .@"inline" => unreachable,
+ .auto => return isByRef(return_type, pt, target),
+ .wasm_watc => {
const ty_classes = abi.classifyType(return_type, pt.zcu);
if (ty_classes[0] == .indirect) return true;
if (ty_classes[0] == .direct and ty_classes[1] == .direct) return true;
@@ -1423,8 +1424,8 @@ fn firstParamSRet(cc: std.builtin.CallingConvention, return_type: Type, pt: Zcu.
/// Lowers a Zig type and its value based on a given calling convention to ensure
/// it matches the ABI.
-fn lowerArg(func: *CodeGen, cc: std.builtin.CallingConvention, ty: Type, value: WValue) !void {
- if (cc != .C) {
+fn lowerArg(func: *CodeGen, cc: std.builtin.NewCallingConvention, ty: Type, value: WValue) !void {
+ if (cc != .wasm_watc) {
return func.lowerToStack(value);
}
@@ -2108,7 +2109,7 @@ fn airRet(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
// to the stack instead
if (func.return_value != .none) {
try func.store(func.return_value, operand, ret_ty, 0);
- } else if (fn_info.cc == .C and ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
+ } else if (fn_info.cc == .wasm_watc and ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
switch (ret_ty.zigTypeTag(zcu)) {
// Aggregate types can be lowered as a singular value
.@"struct", .@"union" => {
@@ -2286,7 +2287,7 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif
} else if (first_param_sret) {
break :result_value sret;
// TODO: Make this less fragile and optimize
- } else if (zcu.typeToFunc(fn_ty).?.cc == .C and ret_ty.zigTypeTag(zcu) == .@"struct" or ret_ty.zigTypeTag(zcu) == .@"union") {
+ } else if (zcu.typeToFunc(fn_ty).?.cc == .wasm_watc and ret_ty.zigTypeTag(zcu) == .@"struct" or ret_ty.zigTypeTag(zcu) == .@"union") {
const result_local = try func.allocLocal(ret_ty);
try func.addLabel(.local_set, result_local.local.value);
const scalar_type = abi.scalarType(ret_ty, zcu);
@@ -2565,7 +2566,7 @@ fn airArg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const arg = func.args[arg_index];
const cc = zcu.typeToFunc(zcu.navValue(func.owner_nav).typeOf(zcu)).?.cc;
const arg_ty = func.typeOfIndex(inst);
- if (cc == .C) {
+ if (cc == .wasm_watc) {
const arg_classes = abi.classifyType(arg_ty, zcu);
for (arg_classes) |class| {
if (class != .none) {
@@ -7175,12 +7176,12 @@ fn callIntrinsic(
// Always pass over C-ABI
const pt = func.pt;
const zcu = pt.zcu;
- var func_type = try genFunctype(func.gpa, .C, param_types, return_type, pt, func.target.*);
+ var func_type = try genFunctype(func.gpa, .{ .wasm_watc = .{} }, param_types, return_type, pt, func.target.*);
defer func_type.deinit(func.gpa);
const func_type_index = try func.bin_file.zigObjectPtr().?.putOrGetFuncType(func.gpa, func_type);
try func.bin_file.addOrUpdateImport(name, symbol_index, null, func_type_index);
- const want_sret_param = firstParamSRet(.C, return_type, pt, func.target.*);
+ const want_sret_param = firstParamSRet(.{ .wasm_watc = .{} }, return_type, pt, func.target.*);
// if we want return as first param, we allocate a pointer to stack,
// and emit it as our first argument
const sret = if (want_sret_param) blk: {
@@ -7193,7 +7194,7 @@ fn callIntrinsic(
for (args, 0..) |arg, arg_i| {
assert(!(want_sret_param and arg == .stack));
assert(Type.fromInterned(param_types[arg_i]).hasRuntimeBitsIgnoreComptime(zcu));
- try func.lowerArg(.C, Type.fromInterned(param_types[arg_i]), arg);
+ try func.lowerArg(.{ .wasm_watc = .{} }, Type.fromInterned(param_types[arg_i]), arg);
}
// Actually call our intrinsic
src/arch/x86_64/abi.zig
@@ -436,62 +436,62 @@ pub const Win64 = struct {
};
pub fn resolveCallingConvention(
- cc: std.builtin.CallingConvention,
+ cc: std.builtin.NewCallingConvention,
target: std.Target,
-) std.builtin.CallingConvention {
+) std.builtin.NewCallingConvention {
return switch (cc) {
- .Unspecified, .C => switch (target.os.tag) {
- else => .SysV,
- .windows => .Win64,
+ .auto => switch (target.os.tag) {
+ else => .{ .x86_64_sysv = .{} },
+ .windows => .{ .x86_64_win = .{} },
},
else => cc,
};
}
-pub fn getCalleePreservedRegs(cc: std.builtin.CallingConvention) []const Register {
+pub fn getCalleePreservedRegs(cc: std.builtin.NewCallingConvention) []const Register {
return switch (cc) {
- .SysV => &SysV.callee_preserved_regs,
- .Win64 => &Win64.callee_preserved_regs,
+ .x86_64_sysv => &SysV.callee_preserved_regs,
+ .x86_64_win => &Win64.callee_preserved_regs,
else => unreachable,
};
}
-pub fn getCallerPreservedRegs(cc: std.builtin.CallingConvention) []const Register {
+pub fn getCallerPreservedRegs(cc: std.builtin.NewCallingConvention) []const Register {
return switch (cc) {
- .SysV => &SysV.caller_preserved_regs,
- .Win64 => &Win64.caller_preserved_regs,
+ .x86_64_sysv => &SysV.caller_preserved_regs,
+ .x86_64_win => &Win64.caller_preserved_regs,
else => unreachable,
};
}
-pub fn getCAbiIntParamRegs(cc: std.builtin.CallingConvention) []const Register {
+pub fn getCAbiIntParamRegs(cc: std.builtin.NewCallingConvention) []const Register {
return switch (cc) {
- .SysV => &SysV.c_abi_int_param_regs,
- .Win64 => &Win64.c_abi_int_param_regs,
+ .x86_64_sysv => &SysV.c_abi_int_param_regs,
+ .x86_64_win => &Win64.c_abi_int_param_regs,
else => unreachable,
};
}
-pub fn getCAbiSseParamRegs(cc: std.builtin.CallingConvention) []const Register {
+pub fn getCAbiSseParamRegs(cc: std.builtin.NewCallingConvention) []const Register {
return switch (cc) {
- .SysV => &SysV.c_abi_sse_param_regs,
- .Win64 => &Win64.c_abi_sse_param_regs,
+ .x86_64_sysv => &SysV.c_abi_sse_param_regs,
+ .x86_64_win => &Win64.c_abi_sse_param_regs,
else => unreachable,
};
}
-pub fn getCAbiIntReturnRegs(cc: std.builtin.CallingConvention) []const Register {
+pub fn getCAbiIntReturnRegs(cc: std.builtin.NewCallingConvention) []const Register {
return switch (cc) {
- .SysV => &SysV.c_abi_int_return_regs,
- .Win64 => &Win64.c_abi_int_return_regs,
+ .x86_64_sysv => &SysV.c_abi_int_return_regs,
+ .x86_64_win => &Win64.c_abi_int_return_regs,
else => unreachable,
};
}
-pub fn getCAbiSseReturnRegs(cc: std.builtin.CallingConvention) []const Register {
+pub fn getCAbiSseReturnRegs(cc: std.builtin.NewCallingConvention) []const Register {
return switch (cc) {
- .SysV => &SysV.c_abi_sse_return_regs,
- .Win64 => &Win64.c_abi_sse_return_regs,
+ .x86_64_sysv => &SysV.c_abi_sse_return_regs,
+ .x86_64_win => &Win64.c_abi_sse_return_regs,
else => unreachable,
};
}
src/arch/x86_64/CodeGen.zig
@@ -918,13 +918,13 @@ pub fn generate(
);
function.va_info = switch (cc) {
else => undefined,
- .SysV => .{ .sysv = .{
+ .x86_64_sysv => .{ .sysv = .{
.gp_count = call_info.gp_count,
.fp_count = call_info.fp_count,
.overflow_arg_area = .{ .index = .args_frame, .off = call_info.stack_byte_count },
.reg_save_area = undefined,
} },
- .Win64 => .{ .win64 = .{} },
+ .x86_64_win => .{ .win64 = .{} },
};
function.gen() catch |err| switch (err) {
@@ -1053,7 +1053,7 @@ pub fn generateLazy(
.bin_file = bin_file,
.allocator = gpa,
.mir = mir,
- .cc = abi.resolveCallingConvention(.Unspecified, function.target.*),
+ .cc = abi.resolveCallingConvention(.auto, function.target.*),
.src_loc = src_loc,
.output_mode = comp.config.output_mode,
.link_mode = comp.config.link_mode,
@@ -1159,7 +1159,7 @@ fn formatWipMir(
.extra = data.self.mir_extra.items,
.frame_locs = (std.MultiArrayList(Mir.FrameLoc){}).slice(),
},
- .cc = .Unspecified,
+ .cc = .auto,
.src_loc = data.self.src_loc,
.output_mode = comp.config.output_mode,
.link_mode = comp.config.link_mode,
@@ -2023,7 +2023,7 @@ fn gen(self: *Self) InnerError!void {
const zcu = pt.zcu;
const fn_info = zcu.typeToFunc(self.fn_type).?;
const cc = abi.resolveCallingConvention(fn_info.cc, self.target.*);
- if (cc != .Naked) {
+ if (cc != .naked) {
try self.asmRegister(.{ ._, .push }, .rbp);
try self.asmPseudoImmediate(.pseudo_cfi_adjust_cfa_offset_i_s, Immediate.s(8));
try self.asmPseudoRegisterImmediate(.pseudo_cfi_rel_offset_ri_s, .rbp, Immediate.s(0));
@@ -2056,7 +2056,7 @@ fn gen(self: *Self) InnerError!void {
}
if (fn_info.is_var_args) switch (cc) {
- .SysV => {
+ .x86_64_sysv => {
const info = &self.va_info.sysv;
const reg_save_area_fi = try self.allocFrameIndex(FrameAlloc.init(.{
.size = abi.SysV.c_abi_int_param_regs.len * 8 +
@@ -2089,7 +2089,7 @@ fn gen(self: *Self) InnerError!void {
self.performReloc(skip_sse_reloc);
},
- .Win64 => return self.fail("TODO implement gen var arg function for Win64", .{}),
+ .x86_64_win => return self.fail("TODO implement gen var arg function for Win64", .{}),
else => unreachable,
};
@@ -2541,7 +2541,7 @@ fn genLazy(self: *Self, lazy_sym: link.File.LazySymbol) InnerError!void {
const enum_ty = Type.fromInterned(lazy_sym.ty);
wip_mir_log.debug("{}.@tagName:", .{enum_ty.fmt(pt)});
- const resolved_cc = abi.resolveCallingConvention(.Unspecified, self.target.*);
+ const resolved_cc = abi.resolveCallingConvention(.auto, self.target.*);
const param_regs = abi.getCAbiIntParamRegs(resolved_cc);
const param_locks = self.register_manager.lockRegsAssumeUnused(2, param_regs[0..2].*);
defer for (param_locks) |lock| self.register_manager.unlockReg(lock);
@@ -2694,7 +2694,7 @@ fn setFrameLoc(
offset.* += self.frame_allocs.items(.abi_size)[frame_i];
}
-fn computeFrameLayout(self: *Self, cc: std.builtin.CallingConvention) !FrameLayout {
+fn computeFrameLayout(self: *Self, cc: std.builtin.NewCallingConvention) !FrameLayout {
const frame_allocs_len = self.frame_allocs.len;
try self.frame_locs.resize(self.gpa, frame_allocs_len);
const stack_frame_order = try self.gpa.alloc(FrameIndex, frame_allocs_len - FrameIndex.named_count);
@@ -3006,11 +3006,10 @@ pub fn spillEflagsIfOccupied(self: *Self) !void {
}
}
-pub fn spillCallerPreservedRegs(self: *Self, cc: std.builtin.CallingConvention) !void {
+pub fn spillCallerPreservedRegs(self: *Self, cc: std.builtin.NewCallingConvention) !void {
switch (cc) {
- inline .SysV, .Win64 => |known_cc| try self.spillRegisters(
- comptime abi.getCallerPreservedRegs(known_cc),
- ),
+ .x86_64_sysv => try self.spillRegisters(abi.getCallerPreservedRegs(.{ .x86_64_sysv = .{} })),
+ .x86_64_win => try self.spillRegisters(abi.getCallerPreservedRegs(.{ .x86_64_win = .{} })),
else => unreachable,
}
}
@@ -12384,7 +12383,7 @@ fn genCall(self: *Self, info: union(enum) {
.lib => |lib| try pt.funcType(.{
.param_types = lib.param_types,
.return_type = lib.return_type,
- .cc = .C,
+ .cc = self.target.defaultCCallingConvention().?,
}),
};
const fn_info = zcu.typeToFunc(fn_ty).?;
@@ -12543,7 +12542,7 @@ fn genCall(self: *Self, info: union(enum) {
src_arg,
.{},
),
- .C, .SysV, .Win64 => {
+ .x86_64_sysv, .x86_64_win => {
const promoted_ty = self.promoteInt(arg_ty);
const promoted_abi_size: u32 = @intCast(promoted_ty.abiSize(zcu));
const dst_alias = registerAlias(dst_reg, promoted_abi_size);
@@ -16822,7 +16821,7 @@ fn airTagName(self: *Self, inst: Air.Inst.Index) !void {
const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
const inst_ty = self.typeOfIndex(inst);
const enum_ty = self.typeOf(un_op);
- const resolved_cc = abi.resolveCallingConvention(.Unspecified, self.target.*);
+ const resolved_cc = abi.resolveCallingConvention(.auto, self.target.*);
// We need a properly aligned and sized call frame to be able to call this function.
{
@@ -18915,7 +18914,7 @@ fn airVaStart(self: *Self, inst: Air.Inst.Index) !void {
self.fn_type.fnCallingConvention(zcu),
self.target.*,
)) {
- .SysV => result: {
+ .x86_64_sysv => result: {
const info = self.va_info.sysv;
const dst_fi = try self.allocFrameIndex(FrameAlloc.initSpill(va_list_ty, zcu));
var field_off: u31 = 0;
@@ -18957,7 +18956,7 @@ fn airVaStart(self: *Self, inst: Air.Inst.Index) !void {
field_off += @intCast(ptr_anyopaque_ty.abiSize(zcu));
break :result .{ .load_frame = .{ .index = dst_fi } };
},
- .Win64 => return self.fail("TODO implement c_va_start for Win64", .{}),
+ .x86_64_win => return self.fail("TODO implement c_va_start for Win64", .{}),
else => unreachable,
};
return self.finishAir(inst, result, .{ .none, .none, .none });
@@ -18976,7 +18975,7 @@ fn airVaArg(self: *Self, inst: Air.Inst.Index) !void {
self.fn_type.fnCallingConvention(zcu),
self.target.*,
)) {
- .SysV => result: {
+ .x86_64_sysv => result: {
try self.spillEflagsIfOccupied();
const tmp_regs =
@@ -19155,7 +19154,7 @@ fn airVaArg(self: *Self, inst: Air.Inst.Index) !void {
);
break :result promote_mcv;
},
- .Win64 => return self.fail("TODO implement c_va_arg for Win64", .{}),
+ .x86_64_win => return self.fail("TODO implement c_va_arg for Win64", .{}),
else => unreachable,
};
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
@@ -19324,12 +19323,12 @@ fn resolveCallingConventionValues(
const resolved_cc = abi.resolveCallingConvention(cc, self.target.*);
switch (cc) {
- .Naked => {
+ .naked => {
assert(result.args.len == 0);
result.return_value = InstTracking.init(.unreach);
result.stack_align = .@"8";
},
- .C, .SysV, .Win64 => {
+ .x86_64_sysv, .x86_64_win => {
var ret_int_reg_i: u32 = 0;
var ret_sse_reg_i: u32 = 0;
var param_int_reg_i: u32 = 0;
@@ -19337,8 +19336,8 @@ fn resolveCallingConventionValues(
result.stack_align = .@"16";
switch (resolved_cc) {
- .SysV => {},
- .Win64 => {
+ .x86_64_sysv => {},
+ .x86_64_win => {
// Align the stack to 16bytes before allocating shadow stack space (if any).
result.stack_byte_count += @intCast(4 * Type.usize.abiSize(zcu));
},
@@ -19356,8 +19355,8 @@ fn resolveCallingConventionValues(
var ret_tracking_i: usize = 0;
const classes = switch (resolved_cc) {
- .SysV => mem.sliceTo(&abi.classifySystemV(ret_ty, zcu, self.target.*, .ret), .none),
- .Win64 => &.{abi.classifyWindows(ret_ty, zcu)},
+ .x86_64_sysv => mem.sliceTo(&abi.classifySystemV(ret_ty, zcu, self.target.*, .ret), .none),
+ .x86_64_win => &.{abi.classifyWindows(ret_ty, zcu)},
else => unreachable,
};
for (classes) |class| switch (class) {
@@ -19419,8 +19418,8 @@ fn resolveCallingConventionValues(
for (param_types, result.args) |ty, *arg| {
assert(ty.hasRuntimeBitsIgnoreComptime(zcu));
switch (resolved_cc) {
- .SysV => {},
- .Win64 => {
+ .x86_64_sysv => {},
+ .x86_64_win => {
param_int_reg_i = @max(param_int_reg_i, param_sse_reg_i);
param_sse_reg_i = param_int_reg_i;
},
@@ -19431,8 +19430,8 @@ fn resolveCallingConventionValues(
var arg_mcv_i: usize = 0;
const classes = switch (resolved_cc) {
- .SysV => mem.sliceTo(&abi.classifySystemV(ty, zcu, self.target.*, .arg), .none),
- .Win64 => &.{abi.classifyWindows(ty, zcu)},
+ .x86_64_sysv => mem.sliceTo(&abi.classifySystemV(ty, zcu, self.target.*, .arg), .none),
+ .x86_64_win => &.{abi.classifyWindows(ty, zcu)},
else => unreachable,
};
for (classes) |class| switch (class) {
@@ -19464,11 +19463,11 @@ fn resolveCallingConventionValues(
},
.sseup => assert(arg_mcv[arg_mcv_i - 1].register.class() == .sse),
.x87, .x87up, .complex_x87, .memory, .win_i128 => switch (resolved_cc) {
- .SysV => switch (class) {
+ .x86_64_sysv => switch (class) {
.x87, .x87up, .complex_x87, .memory => break,
else => unreachable,
},
- .Win64 => if (ty.abiSize(zcu) > 8) {
+ .x86_64_win => if (ty.abiSize(zcu) > 8) {
const param_int_reg =
abi.getCAbiIntParamRegs(resolved_cc)[param_int_reg_i].to64();
param_int_reg_i += 1;
@@ -19530,7 +19529,7 @@ fn resolveCallingConventionValues(
assert(param_sse_reg_i <= 16);
result.fp_count = param_sse_reg_i;
},
- .Unspecified => {
+ .auto => {
result.stack_align = .@"16";
// Return values
src/arch/x86_64/Lower.zig
@@ -6,7 +6,7 @@ link_mode: std.builtin.LinkMode,
pic: bool,
allocator: std.mem.Allocator,
mir: Mir,
-cc: std.builtin.CallingConvention,
+cc: std.builtin.NewCallingConvention,
err_msg: ?*Zcu.ErrorMsg = null,
src_loc: Zcu.LazySrcLoc,
result_insts_len: u8 = undefined,
src/codegen/llvm/Builder.zig
@@ -2052,6 +2052,7 @@ pub const CallConv = enum(u10) {
x86_intrcc,
avr_intrcc,
avr_signalcc,
+ avr_builtincc,
amdgpu_vs = 87,
amdgpu_gs,
@@ -2060,6 +2061,7 @@ pub const CallConv = enum(u10) {
amdgpu_kernel,
x86_regcallcc,
amdgpu_hs,
+ msp430_builtincc,
amdgpu_ls = 95,
amdgpu_es,
@@ -2068,9 +2070,15 @@ pub const CallConv = enum(u10) {
amdgpu_gfx = 100,
+ m68k_intrcc,
+
aarch64_sme_preservemost_from_x0 = 102,
aarch64_sme_preservemost_from_x2,
+ m68k_rtdcc = 106,
+
+ riscv_vectorcallcc = 110,
+
_,
pub const default = CallConv.ccc;
@@ -2115,6 +2123,7 @@ pub const CallConv = enum(u10) {
.x86_intrcc,
.avr_intrcc,
.avr_signalcc,
+ .avr_builtincc,
.amdgpu_vs,
.amdgpu_gs,
.amdgpu_ps,
@@ -2122,13 +2131,17 @@ pub const CallConv = enum(u10) {
.amdgpu_kernel,
.x86_regcallcc,
.amdgpu_hs,
+ .msp430_builtincc,
.amdgpu_ls,
.amdgpu_es,
.aarch64_vector_pcs,
.aarch64_sve_vector_pcs,
.amdgpu_gfx,
+ .m68k_intrcc,
.aarch64_sme_preservemost_from_x0,
.aarch64_sme_preservemost_from_x2,
+ .m68k_rtdcc,
+ .riscv_vectorcallcc,
=> try writer.print(" {s}", .{@tagName(self)}),
_ => try writer.print(" cc{d}", .{@intFromEnum(self)}),
}
src/codegen/c.zig
@@ -1783,7 +1783,7 @@ pub const DeclGen = struct {
const fn_ctype = try dg.ctypeFromType(fn_ty, kind);
const fn_info = zcu.typeToFunc(fn_ty).?;
- if (fn_info.cc == .Naked) {
+ if (fn_info.cc == .naked) {
switch (kind) {
.forward => try w.writeAll("zig_naked_decl "),
.complete => try w.writeAll("zig_naked "),
@@ -1796,7 +1796,7 @@ pub const DeclGen = struct {
var trailing = try renderTypePrefix(dg.pass, &dg.ctype_pool, zcu, w, fn_ctype, .suffix, .{});
- if (toCallingConvention(fn_info.cc)) |call_conv| {
+ if (toCallingConvention(fn_info.cc, zcu)) |call_conv| {
try w.print("{}zig_callconv({s})", .{ trailing, call_conv });
trailing = .maybe_space;
}
@@ -7604,12 +7604,17 @@ fn writeMemoryOrder(w: anytype, order: std.builtin.AtomicOrder) !void {
return w.writeAll(toMemoryOrder(order));
}
-fn toCallingConvention(call_conv: std.builtin.CallingConvention) ?[]const u8 {
- return switch (call_conv) {
- .Stdcall => "stdcall",
- .Fastcall => "fastcall",
- .Vectorcall => "vectorcall",
- else => null,
+fn toCallingConvention(cc: std.builtin.NewCallingConvention, zcu: *Zcu) ?[]const u8 {
+ return switch (cc) {
+ .auto, .naked => null,
+ .x86_stdcall => "stdcall",
+ .x86_fastcall => "fastcall",
+ .x86_vectorcall, .x86_64_vectorcall => "vectorcall",
+ else => {
+ // `Zcu.callconvSupported` means this must be the C callconv.
+ assert(cc.eql(zcu.getTarget().defaultCCallingConvention().?));
+ return null;
+ },
};
}
src/codegen/llvm.zig
@@ -1159,7 +1159,7 @@ pub const Object = struct {
}
{
- var module_flags = try std.ArrayList(Builder.Metadata).initCapacity(o.gpa, 6);
+ var module_flags = try std.ArrayList(Builder.Metadata).initCapacity(o.gpa, 7);
defer module_flags.deinit();
const behavior_error = try o.builder.metadataConstant(try o.builder.intConst(.i32, 1));
@@ -1233,6 +1233,18 @@ pub const Object = struct {
}
}
+ const target = comp.root_mod.resolved_target.result;
+ if (target.os.tag == .windows and (target.cpu.arch == .x86_64 or target.cpu.arch == .x86)) {
+ // Add the "RegCallv4" flag so that any functions using `x86_regcallcc` use regcall
+ // v4, which is essentially a requirement on Windows. See corresponding logic in
+ // `toLlvmCallConvTag`.
+ module_flags.appendAssumeCapacity(try o.builder.metadataModuleFlag(
+ behavior_max,
+ try o.builder.metadataString("RegCallv4"),
+ try o.builder.metadataConstant(.@"1"),
+ ));
+ }
+
try o.builder.metadataNamed(try o.builder.metadataString("llvm.module.flags"), module_flags.items);
}
@@ -1467,14 +1479,6 @@ pub const Object = struct {
_ = try attributes.removeFnAttr(.@"noinline");
}
- const stack_alignment = func.analysisUnordered(ip).stack_alignment;
- if (stack_alignment != .none) {
- try attributes.addFnAttr(.{ .alignstack = stack_alignment.toLlvm() }, &o.builder);
- try attributes.addFnAttr(.@"noinline", &o.builder);
- } else {
- _ = try attributes.removeFnAttr(.alignstack);
- }
-
if (func_analysis.branch_hint == .cold) {
try attributes.addFnAttr(.cold, &o.builder);
} else {
@@ -1486,7 +1490,7 @@ pub const Object = struct {
} else {
_ = try attributes.removeFnAttr(.sanitize_thread);
}
- const is_naked = fn_info.cc == .Naked;
+ const is_naked = fn_info.cc == .naked;
if (owner_mod.fuzz and !func_analysis.disable_instrumentation and !is_naked) {
try attributes.addFnAttr(.optforfuzzing, &o.builder);
_ = try attributes.removeFnAttr(.skipprofile);
@@ -1784,7 +1788,7 @@ pub const Object = struct {
.liveness = liveness,
.ng = &ng,
.wip = wip,
- .is_naked = fn_info.cc == .Naked,
+ .is_naked = fn_info.cc == .naked,
.fuzz = fuzz,
.ret_ptr = ret_ptr,
.args = args.items,
@@ -3038,14 +3042,33 @@ pub const Object = struct {
llvm_arg_i += 1;
}
- switch (fn_info.cc) {
- .Unspecified, .Inline => function_index.setCallConv(.fastcc, &o.builder),
- .Naked => try attributes.addFnAttr(.naked, &o.builder),
- .Async => {
- function_index.setCallConv(.fastcc, &o.builder);
- @panic("TODO: LLVM backend lower async function");
- },
- else => function_index.setCallConv(toLlvmCallConv(fn_info.cc, target), &o.builder),
+ if (fn_info.cc == .@"async") {
+ @panic("TODO: LLVM backend lower async function");
+ }
+
+ {
+ const cc_info = toLlvmCallConv(fn_info.cc, target).?;
+
+ function_index.setCallConv(cc_info.llvm_cc, &o.builder);
+
+ if (cc_info.align_stack) {
+ try attributes.addFnAttr(.{ .alignstack = .fromByteUnits(target.stackAlignment()) }, &o.builder);
+ } else {
+ _ = try attributes.removeFnAttr(.alignstack);
+ }
+
+ if (cc_info.naked) {
+ try attributes.addFnAttr(.naked, &o.builder);
+ } else {
+ _ = try attributes.removeFnAttr(.naked);
+ }
+
+ for (0..cc_info.inreg_param_count) |param_idx| {
+ try attributes.addParamAttr(param_idx, .inreg, &o.builder);
+ }
+ for (cc_info.inreg_param_count..std.math.maxInt(u2)) |param_idx| {
+ _ = try attributes.removeParamAttr(param_idx, .inreg);
+ }
}
if (resolved.alignment != .none)
@@ -3061,7 +3084,7 @@ pub const Object = struct {
// suppress generation of the prologue and epilogue, and the prologue is where the
// frame pointer normally gets set up. At time of writing, this is the case for at
// least x86 and RISC-V.
- owner_mod.omit_frame_pointer or fn_info.cc == .Naked,
+ owner_mod.omit_frame_pointer or fn_info.cc == .naked,
);
if (fn_info.return_type == .noreturn_type) try attributes.addFnAttr(.noreturn, &o.builder);
@@ -4618,9 +4641,16 @@ pub const Object = struct {
if (!param_ty.isPtrLikeOptional(zcu) and !ptr_info.flags.is_allowzero) {
try attributes.addParamAttr(llvm_arg_i, .nonnull, &o.builder);
}
- if (fn_info.cc == .Interrupt) {
- const child_type = try lowerType(o, Type.fromInterned(ptr_info.child));
- try attributes.addParamAttr(llvm_arg_i, .{ .byval = child_type }, &o.builder);
+ switch (fn_info.cc) {
+ else => {},
+ .x86_64_interrupt,
+ .x86_interrupt,
+ .avr_interrupt,
+ .m68k_interrupt,
+ => {
+ const child_type = try lowerType(o, Type.fromInterned(ptr_info.child));
+ try attributes.addParamAttr(llvm_arg_i, .{ .byval = child_type }, &o.builder);
+ },
}
if (ptr_info.flags.is_const) {
try attributes.addParamAttr(llvm_arg_i, .readonly, &o.builder);
@@ -5677,7 +5707,7 @@ pub const FuncGen = struct {
.always_tail => .musttail,
.async_kw, .no_async, .always_inline, .compile_time => unreachable,
},
- toLlvmCallConv(fn_info.cc, target),
+ toLlvmCallConvTag(fn_info.cc, target).?,
try attributes.finish(&o.builder),
try o.lowerType(zig_fn_ty),
llvm_fn,
@@ -5756,7 +5786,7 @@ pub const FuncGen = struct {
_ = try fg.wip.callIntrinsicAssumeCold();
_ = try fg.wip.call(
.normal,
- toLlvmCallConv(fn_info.cc, target),
+ toLlvmCallConvTag(fn_info.cc, target).?,
.none,
panic_global.typeOf(&o.builder),
panic_global.toValue(&o.builder),
@@ -11554,36 +11584,146 @@ fn toLlvmAtomicRmwBinOp(
};
}
-fn toLlvmCallConv(cc: std.builtin.CallingConvention, target: std.Target) Builder.CallConv {
- return switch (cc) {
- .Unspecified, .Inline, .Async => .fastcc,
- .C, .Naked => .ccc,
- .Stdcall => .x86_stdcallcc,
- .Fastcall => .x86_fastcallcc,
- .Vectorcall => return switch (target.cpu.arch) {
- .x86, .x86_64 => .x86_vectorcallcc,
- .aarch64, .aarch64_be => .aarch64_vector_pcs,
- else => unreachable,
- },
- .Thiscall => .x86_thiscallcc,
- .APCS => .arm_apcscc,
- .AAPCS => .arm_aapcscc,
- .AAPCSVFP => .arm_aapcs_vfpcc,
- .Interrupt => return switch (target.cpu.arch) {
- .x86, .x86_64 => .x86_intrcc,
- .avr => .avr_intrcc,
- .msp430 => .msp430_intrcc,
- else => unreachable,
- },
- .Signal => .avr_signalcc,
- .SysV => .x86_64_sysvcc,
- .Win64 => .win64cc,
- .Kernel => return switch (target.cpu.arch) {
- .nvptx, .nvptx64 => .ptx_kernel,
- .amdgcn => .amdgpu_kernel,
+const CallingConventionInfo = struct {
+ /// The LLVM calling convention to use.
+ llvm_cc: Builder.CallConv,
+ /// Whether to use an `alignstack` attribute to forcibly re-align the stack pointer in the function's prologue.
+ align_stack: bool,
+ /// Whether the function needs a `naked` attribute.
+ naked: bool,
+ /// How many leading parameters to apply the `inreg` attribute to.
+ inreg_param_count: u2 = 0,
+};
+
+pub fn toLlvmCallConv(cc: std.builtin.NewCallingConvention, target: std.Target) ?CallingConventionInfo {
+ const llvm_cc = toLlvmCallConvTag(cc, target) orelse return null;
+ const incoming_stack_alignment: ?u64, const register_params: u2 = switch (cc) {
+ inline else => |pl| switch (@TypeOf(pl)) {
+ void => .{ null, 0 },
+ std.builtin.NewCallingConvention.CommonOptions => .{ pl.incoming_stack_alignment, 0 },
+ std.builtin.NewCallingConvention.X86RegparmOptions => .{ pl.incoming_stack_alignment, pl.register_params },
else => unreachable,
},
- .Vertex, .Fragment => unreachable,
+ };
+ return .{
+ .llvm_cc = llvm_cc,
+ .align_stack = if (incoming_stack_alignment) |a| need_align: {
+ const normal_stack_align = target.stackAlignment();
+ break :need_align a < normal_stack_align;
+ } else false,
+ .naked = cc == .naked,
+ .inreg_param_count = register_params,
+ };
+}
+fn toLlvmCallConvTag(cc_tag: std.builtin.NewCallingConvention.Tag, target: std.Target) ?Builder.CallConv {
+ if (target.defaultCCallingConvention()) |default_c| {
+ if (cc_tag == default_c) {
+ return .ccc;
+ }
+ }
+ return switch (cc_tag) {
+ .@"inline" => unreachable,
+ .auto, .@"async" => .fastcc,
+ .naked => .ccc,
+ .x86_64_sysv => .x86_64_sysvcc,
+ .x86_64_win => .win64cc,
+ .x86_64_regcall_v3_sysv => if (target.cpu.arch == .x86_64 and target.os.tag != .windows)
+ .x86_regcallcc
+ else
+ null,
+ .x86_64_regcall_v4_win => if (target.cpu.arch == .x86_64 and target.os.tag == .windows)
+ .x86_regcallcc // we use the "RegCallv4" module flag to make this correct
+ else
+ null,
+ .x86_64_vectorcall => .x86_vectorcallcc,
+ .x86_64_interrupt => .x86_intrcc,
+ .x86_stdcall => .x86_stdcallcc,
+ .x86_fastcall => .x86_fastcallcc,
+ .x86_thiscall => .x86_thiscallcc,
+ .x86_regcall_v3 => if (target.cpu.arch == .x86 and target.os.tag != .windows)
+ .x86_regcallcc
+ else
+ null,
+ .x86_regcall_v4_win => if (target.cpu.arch == .x86 and target.os.tag == .windows)
+ .x86_regcallcc // we use the "RegCallv4" module flag to make this correct
+ else
+ null,
+ .x86_vectorcall => .x86_vectorcallcc,
+ .x86_interrupt => .x86_intrcc,
+ .aarch64_vfabi => .aarch64_vector_pcs,
+ .aarch64_vfabi_sve => .aarch64_sve_vector_pcs,
+ .arm_apcs => .arm_apcscc,
+ .arm_aapcs => .arm_aapcscc,
+ .arm_aapcs_vfp => .arm_aapcs_vfpcc,
+ .riscv64_lp64_v => .riscv_vectorcallcc,
+ .riscv32_ilp32_v => .riscv_vectorcallcc,
+ .avr_builtin => .avr_builtincc,
+ .avr_signal => .avr_signalcc,
+ .avr_interrupt => .avr_intrcc,
+ .m68k_rtd => .m68k_rtdcc,
+ .m68k_interrupt => .m68k_intrcc,
+ .amdgcn_kernel => .amdgpu_kernel,
+ .amdgcn_cs => .amdgpu_cs,
+ .nvptx_device => .ptx_device,
+ .nvptx_kernel => .ptx_kernel,
+
+ // All the calling conventions which LLVM does not have a general representation for.
+ // Note that these are often still supported through the `defaultCCallingConvention` path above via `ccc`.
+ .x86_sysv,
+ .x86_win,
+ .x86_thiscall_mingw,
+ .aarch64_aapcs,
+ .aarch64_aapcs_darwin,
+ .aarch64_aapcs_win,
+ .arm_aapcs16_vfp,
+ .arm_interrupt,
+ .mips64_n64,
+ .mips64_n32,
+ .mips64_interrupt,
+ .mips_o32,
+ .mips_interrupt,
+ .riscv64_lp64,
+ .riscv64_interrupt,
+ .riscv32_ilp32,
+ .riscv32_interrupt,
+ .sparc64_sysv,
+ .sparc_sysv,
+ .powerpc64_elf,
+ .powerpc64_elf_altivec,
+ .powerpc64_elf_v2,
+ .powerpc_sysv,
+ .powerpc_sysv_altivec,
+ .powerpc_aix,
+ .powerpc_aix_altivec,
+ .wasm_watc,
+ .arc_sysv,
+ .avr_gnu,
+ .bpf_std,
+ .csky_sysv,
+ .csky_interrupt,
+ .hexagon_sysv,
+ .hexagon_sysv_hvx,
+ .lanai_sysv,
+ .loongarch64_lp64,
+ .loongarch32_ilp32,
+ .m68k_sysv,
+ .m68k_gnu,
+ .msp430_eabi,
+ .propeller1_sysv,
+ .propeller2_sysv,
+ .s390x_sysv,
+ .s390x_sysv_vx,
+ .ve_sysv,
+ .xcore_xs1,
+ .xcore_xs2,
+ .xtensa_call0,
+ .xtensa_windowed,
+ .amdgcn_device,
+ .spirv_device,
+ .spirv_kernel,
+ .spirv_fragment,
+ .spirv_vertex,
+ => null,
};
}
@@ -11711,31 +11851,27 @@ fn firstParamSRet(fn_info: InternPool.Key.FuncType, zcu: *Zcu, target: std.Targe
if (!return_type.hasRuntimeBitsIgnoreComptime(zcu)) return false;
return switch (fn_info.cc) {
- .Unspecified, .Inline => returnTypeByRef(zcu, target, return_type),
- .C => switch (target.cpu.arch) {
- .mips, .mipsel => switch (mips_c_abi.classifyType(return_type, zcu, .ret)) {
- .memory, .i32_array => true,
- .byval => false,
- },
- .x86 => isByRef(return_type, zcu),
- .x86_64 => switch (target.os.tag) {
- .windows => x86_64_abi.classifyWindows(return_type, zcu) == .memory,
- else => firstParamSRetSystemV(return_type, zcu, target),
- },
- .wasm32 => wasm_c_abi.classifyType(return_type, zcu)[0] == .indirect,
- .aarch64, .aarch64_be => aarch64_c_abi.classifyType(return_type, zcu) == .memory,
- .arm, .armeb => switch (arm_c_abi.classifyType(return_type, zcu, .ret)) {
- .memory, .i64_array => true,
- .i32_array => |size| size != 1,
- .byval => false,
- },
- .riscv32, .riscv64 => riscv_c_abi.classifyType(return_type, zcu) == .memory,
- else => false, // TODO investigate C ABI for other architectures
+ .auto => returnTypeByRef(zcu, target, return_type),
+ .x86_64_sysv => firstParamSRetSystemV(return_type, zcu, target),
+ .x86_64_win => x86_64_abi.classifyWindows(return_type, zcu) == .memory,
+ .x86_sysv, .x86_win => isByRef(return_type, zcu),
+ .x86_stdcall => !isScalar(zcu, return_type),
+ .wasm_watc => wasm_c_abi.classifyType(return_type, zcu)[0] == .indirect,
+ .aarch64_aapcs,
+ .aarch64_aapcs_darwin,
+ .aarch64_aapcs_win,
+ => aarch64_c_abi.classifyType(return_type, zcu) == .memory,
+ .arm_aapcs => switch (arm_c_abi.classifyType(return_type, zcu, .ret)) {
+ .memory, .i64_array => true,
+ .i32_array => |size| size != 1,
+ .byval => false,
},
- .SysV => firstParamSRetSystemV(return_type, zcu, target),
- .Win64 => x86_64_abi.classifyWindows(return_type, zcu) == .memory,
- .Stdcall => !isScalar(zcu, return_type),
- else => false,
+ .riscv64_lp64, .riscv32_ilp32 => riscv_c_abi.classifyType(return_type, zcu) == .memory,
+ .mips64_n64, .mips64_n32, .mips_o32 => switch (mips_c_abi.classifyType(return_type, zcu, .ret)) {
+ .memory, .i32_array => true,
+ .byval => false,
+ },
+ else => false, // TODO: investigate other targets/callconvs
};
}
@@ -11761,82 +11897,64 @@ fn lowerFnRetTy(o: *Object, fn_info: InternPool.Key.FuncType) Allocator.Error!Bu
}
const target = zcu.getTarget();
switch (fn_info.cc) {
- .Unspecified,
- .Inline,
- => return if (returnTypeByRef(zcu, target, return_type)) .void else o.lowerType(return_type),
-
- .C => {
- switch (target.cpu.arch) {
- .mips, .mipsel => {
- switch (mips_c_abi.classifyType(return_type, zcu, .ret)) {
- .memory, .i32_array => return .void,
- .byval => return o.lowerType(return_type),
- }
- },
- .x86 => return if (isByRef(return_type, zcu)) .void else o.lowerType(return_type),
- .x86_64 => switch (target.os.tag) {
- .windows => return lowerWin64FnRetTy(o, fn_info),
- else => return lowerSystemVFnRetTy(o, fn_info),
- },
- .wasm32 => {
- if (isScalar(zcu, return_type)) {
- return o.lowerType(return_type);
- }
- const classes = wasm_c_abi.classifyType(return_type, zcu);
- if (classes[0] == .indirect or classes[0] == .none) {
- return .void;
- }
-
- assert(classes[0] == .direct and classes[1] == .none);
- const scalar_type = wasm_c_abi.scalarType(return_type, zcu);
- return o.builder.intType(@intCast(scalar_type.abiSize(zcu) * 8));
- },
- .aarch64, .aarch64_be => {
- switch (aarch64_c_abi.classifyType(return_type, zcu)) {
- .memory => return .void,
- .float_array => return o.lowerType(return_type),
- .byval => return o.lowerType(return_type),
- .integer => return o.builder.intType(@intCast(return_type.bitSize(zcu))),
- .double_integer => return o.builder.arrayType(2, .i64),
- }
- },
- .arm, .armeb => {
- switch (arm_c_abi.classifyType(return_type, zcu, .ret)) {
- .memory, .i64_array => return .void,
- .i32_array => |len| return if (len == 1) .i32 else .void,
- .byval => return o.lowerType(return_type),
- }
- },
- .riscv32, .riscv64 => {
- switch (riscv_c_abi.classifyType(return_type, zcu)) {
- .memory => return .void,
- .integer => {
- return o.builder.intType(@intCast(return_type.bitSize(zcu)));
- },
- .double_integer => {
- return o.builder.structType(.normal, &.{ .i64, .i64 });
- },
- .byval => return o.lowerType(return_type),
- .fields => {
- var types_len: usize = 0;
- var types: [8]Builder.Type = undefined;
- for (0..return_type.structFieldCount(zcu)) |field_index| {
- const field_ty = return_type.fieldType(field_index, zcu);
- if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue;
- types[types_len] = try o.lowerType(field_ty);
- types_len += 1;
- }
- return o.builder.structType(.normal, types[0..types_len]);
- },
- }
- },
- // TODO investigate C ABI for other architectures
- else => return o.lowerType(return_type),
+ .@"inline" => unreachable,
+ .auto => return if (returnTypeByRef(zcu, target, return_type)) .void else o.lowerType(return_type),
+
+ .x86_64_sysv => return lowerSystemVFnRetTy(o, fn_info),
+ .x86_64_win => return lowerWin64FnRetTy(o, fn_info),
+ .x86_stdcall => return if (isScalar(zcu, return_type)) o.lowerType(return_type) else .void,
+ .x86_sysv, .x86_win => return if (isByRef(return_type, zcu)) .void else o.lowerType(return_type),
+ .aarch64_aapcs, .aarch64_aapcs_darwin, .aarch64_aapcs_win => switch (aarch64_c_abi.classifyType(return_type, zcu)) {
+ .memory => return .void,
+ .float_array => return o.lowerType(return_type),
+ .byval => return o.lowerType(return_type),
+ .integer => return o.builder.intType(@intCast(return_type.bitSize(zcu))),
+ .double_integer => return o.builder.arrayType(2, .i64),
+ },
+ .arm_aapcs => switch (arm_c_abi.classifyType(return_type, zcu, .ret)) {
+ .memory, .i64_array => return .void,
+ .i32_array => |len| return if (len == 1) .i32 else .void,
+ .byval => return o.lowerType(return_type),
+ },
+ .mips64_n64, .mips64_n32, .mips_o32 => switch (mips_c_abi.classifyType(return_type, zcu, .ret)) {
+ .memory, .i32_array => return .void,
+ .byval => return o.lowerType(return_type),
+ },
+ .riscv64_lp64, .riscv32_ilp32 => switch (riscv_c_abi.classifyType(return_type, zcu)) {
+ .memory => return .void,
+ .integer => {
+ return o.builder.intType(@intCast(return_type.bitSize(zcu)));
+ },
+ .double_integer => {
+ return o.builder.structType(.normal, &.{ .i64, .i64 });
+ },
+ .byval => return o.lowerType(return_type),
+ .fields => {
+ var types_len: usize = 0;
+ var types: [8]Builder.Type = undefined;
+ for (0..return_type.structFieldCount(zcu)) |field_index| {
+ const field_ty = return_type.fieldType(field_index, zcu);
+ if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue;
+ types[types_len] = try o.lowerType(field_ty);
+ types_len += 1;
+ }
+ return o.builder.structType(.normal, types[0..types_len]);
+ },
+ },
+ .wasm_watc => {
+ if (isScalar(zcu, return_type)) {
+ return o.lowerType(return_type);
+ }
+ const classes = wasm_c_abi.classifyType(return_type, zcu);
+ if (classes[0] == .indirect or classes[0] == .none) {
+ return .void;
}
+
+ assert(classes[0] == .direct and classes[1] == .none);
+ const scalar_type = wasm_c_abi.scalarType(return_type, zcu);
+ return o.builder.intType(@intCast(scalar_type.abiSize(zcu) * 8));
},
- .Win64 => return lowerWin64FnRetTy(o, fn_info),
- .SysV => return lowerSystemVFnRetTy(o, fn_info),
- .Stdcall => return if (isScalar(zcu, return_type)) o.lowerType(return_type) else .void,
+ // TODO investigate other callconvs
else => return o.lowerType(return_type),
}
}
@@ -11989,7 +12107,8 @@ const ParamTypeIterator = struct {
return .no_bits;
}
switch (it.fn_info.cc) {
- .Unspecified, .Inline => {
+ .@"inline" => unreachable,
+ .auto => {
it.zig_index += 1;
it.llvm_index += 1;
if (ty.isSlice(zcu) or
@@ -12010,97 +12129,12 @@ const ParamTypeIterator = struct {
return .byval;
}
},
- .Async => {
+ .@"async" => {
@panic("TODO implement async function lowering in the LLVM backend");
},
- .C => switch (target.cpu.arch) {
- .mips, .mipsel => {
- it.zig_index += 1;
- it.llvm_index += 1;
- switch (mips_c_abi.classifyType(ty, zcu, .arg)) {
- .memory => {
- it.byval_attr = true;
- return .byref;
- },
- .byval => return .byval,
- .i32_array => |size| return Lowering{ .i32_array = size },
- }
- },
- .x86_64 => switch (target.os.tag) {
- .windows => return it.nextWin64(ty),
- else => return it.nextSystemV(ty),
- },
- .wasm32 => {
- it.zig_index += 1;
- it.llvm_index += 1;
- if (isScalar(zcu, ty)) {
- return .byval;
- }
- const classes = wasm_c_abi.classifyType(ty, zcu);
- if (classes[0] == .indirect) {
- return .byref;
- }
- return .abi_sized_int;
- },
- .aarch64, .aarch64_be => {
- it.zig_index += 1;
- it.llvm_index += 1;
- switch (aarch64_c_abi.classifyType(ty, zcu)) {
- .memory => return .byref_mut,
- .float_array => |len| return Lowering{ .float_array = len },
- .byval => return .byval,
- .integer => {
- it.types_len = 1;
- it.types_buffer[0] = .i64;
- return .multiple_llvm_types;
- },
- .double_integer => return Lowering{ .i64_array = 2 },
- }
- },
- .arm, .armeb => {
- it.zig_index += 1;
- it.llvm_index += 1;
- switch (arm_c_abi.classifyType(ty, zcu, .arg)) {
- .memory => {
- it.byval_attr = true;
- return .byref;
- },
- .byval => return .byval,
- .i32_array => |size| return Lowering{ .i32_array = size },
- .i64_array => |size| return Lowering{ .i64_array = size },
- }
- },
- .riscv32, .riscv64 => {
- it.zig_index += 1;
- it.llvm_index += 1;
- switch (riscv_c_abi.classifyType(ty, zcu)) {
- .memory => return .byref_mut,
- .byval => return .byval,
- .integer => return .abi_sized_int,
- .double_integer => return Lowering{ .i64_array = 2 },
- .fields => {
- it.types_len = 0;
- for (0..ty.structFieldCount(zcu)) |field_index| {
- const field_ty = ty.fieldType(field_index, zcu);
- if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue;
- it.types_buffer[it.types_len] = try it.object.lowerType(field_ty);
- it.types_len += 1;
- }
- it.llvm_index += it.types_len - 1;
- return .multiple_llvm_types;
- },
- }
- },
- // TODO investigate C ABI for other architectures
- else => {
- it.zig_index += 1;
- it.llvm_index += 1;
- return .byval;
- },
- },
- .Win64 => return it.nextWin64(ty),
- .SysV => return it.nextSystemV(ty),
- .Stdcall => {
+ .x86_64_sysv => return it.nextSystemV(ty),
+ .x86_64_win => return it.nextWin64(ty),
+ .x86_stdcall => {
it.zig_index += 1;
it.llvm_index += 1;
@@ -12111,6 +12145,80 @@ const ParamTypeIterator = struct {
return .byref;
}
},
+ .aarch64_aapcs, .aarch64_aapcs_darwin, .aarch64_aapcs_win => {
+ it.zig_index += 1;
+ it.llvm_index += 1;
+ switch (aarch64_c_abi.classifyType(ty, zcu)) {
+ .memory => return .byref_mut,
+ .float_array => |len| return Lowering{ .float_array = len },
+ .byval => return .byval,
+ .integer => {
+ it.types_len = 1;
+ it.types_buffer[0] = .i64;
+ return .multiple_llvm_types;
+ },
+ .double_integer => return Lowering{ .i64_array = 2 },
+ }
+ },
+ .arm_aapcs => {
+ it.zig_index += 1;
+ it.llvm_index += 1;
+ switch (arm_c_abi.classifyType(ty, zcu, .arg)) {
+ .memory => {
+ it.byval_attr = true;
+ return .byref;
+ },
+ .byval => return .byval,
+ .i32_array => |size| return Lowering{ .i32_array = size },
+ .i64_array => |size| return Lowering{ .i64_array = size },
+ }
+ },
+ .mips64_n64, .mips64_n32, .mips_o32 => {
+ it.zig_index += 1;
+ it.llvm_index += 1;
+ switch (mips_c_abi.classifyType(ty, zcu, .arg)) {
+ .memory => {
+ it.byval_attr = true;
+ return .byref;
+ },
+ .byval => return .byval,
+ .i32_array => |size| return Lowering{ .i32_array = size },
+ }
+ },
+ .riscv64_lp64, .riscv32_ilp32 => {
+ it.zig_index += 1;
+ it.llvm_index += 1;
+ switch (riscv_c_abi.classifyType(ty, zcu)) {
+ .memory => return .byref_mut,
+ .byval => return .byval,
+ .integer => return .abi_sized_int,
+ .double_integer => return Lowering{ .i64_array = 2 },
+ .fields => {
+ it.types_len = 0;
+ for (0..ty.structFieldCount(zcu)) |field_index| {
+ const field_ty = ty.fieldType(field_index, zcu);
+ if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue;
+ it.types_buffer[it.types_len] = try it.object.lowerType(field_ty);
+ it.types_len += 1;
+ }
+ it.llvm_index += it.types_len - 1;
+ return .multiple_llvm_types;
+ },
+ }
+ },
+ .wasm_watc => {
+ it.zig_index += 1;
+ it.llvm_index += 1;
+ if (isScalar(zcu, ty)) {
+ return .byval;
+ }
+ const classes = wasm_c_abi.classifyType(ty, zcu);
+ if (classes[0] == .indirect) {
+ return .byref;
+ }
+ return .abi_sized_int;
+ },
+ // TODO investigate other callconvs
else => {
it.zig_index += 1;
it.llvm_index += 1;
@@ -12263,13 +12371,13 @@ fn iterateParamTypes(object: *Object, fn_info: InternPool.Key.FuncType) ParamTyp
}
fn ccAbiPromoteInt(
- cc: std.builtin.CallingConvention,
+ cc: std.builtin.NewCallingConvention,
zcu: *Zcu,
ty: Type,
) ?std.builtin.Signedness {
const target = zcu.getTarget();
switch (cc) {
- .Unspecified, .Inline, .Async => return null,
+ .auto, .@"inline", .@"async" => return null,
else => {},
}
const int_info = switch (ty.zigTypeTag(zcu)) {
src/codegen/spirv.zig
@@ -1640,8 +1640,8 @@ const NavGen = struct {
comptime assert(zig_call_abi_ver == 3);
switch (fn_info.cc) {
- .Unspecified, .Kernel, .Fragment, .Vertex, .C => {},
- else => unreachable, // TODO
+ .auto, .spirv_kernel, .spirv_fragment, .spirv_vertex => {},
+ else => @panic("TODO"),
}
// TODO: Put this somewhere in Sema.zig
@@ -2970,7 +2970,7 @@ const NavGen = struct {
.id_result_type = return_ty_id,
.id_result = result_id,
.function_control = switch (fn_info.cc) {
- .Inline => .{ .Inline = true },
+ .@"inline" => .{ .Inline = true },
else => .{},
},
.function_type = prototype_ty_id,
src/link/C.zig
@@ -217,7 +217,7 @@ pub fn updateFunc(
.mod = zcu.navFileScope(func.owner_nav).mod,
.error_msg = null,
.pass = .{ .nav = func.owner_nav },
- .is_naked_fn = zcu.navValue(func.owner_nav).typeOf(zcu).fnCallingConvention(zcu) == .Naked,
+ .is_naked_fn = zcu.navValue(func.owner_nav).typeOf(zcu).fnCallingConvention(zcu) == .naked,
.fwd_decl = fwd_decl.toManaged(gpa),
.ctype_pool = ctype_pool.*,
.scratch = .{},
src/link/Coff.zig
@@ -1484,14 +1484,16 @@ pub fn updateExports(
const exported_nav = ip.getNav(exported_nav_index);
const exported_ty = exported_nav.typeOf(ip);
if (!ip.isFunctionType(exported_ty)) continue;
- const winapi_cc: std.builtin.CallingConvention = switch (target.cpu.arch) {
- .x86 => .Stdcall,
- else => .C,
+ const c_cc = target.defaultCCallingConvention().?;
+ const winapi_cc: std.builtin.NewCallingConvention = switch (target.cpu.arch) {
+ .x86 => .{ .x86_stdcall = .{} },
+ else => c_cc,
};
const exported_cc = Type.fromInterned(exported_ty).fnCallingConvention(zcu);
- if (exported_cc == .C and exp.opts.name.eqlSlice("main", ip) and comp.config.link_libc) {
+ const CcTag = std.builtin.NewCallingConvention.Tag;
+ if (@as(CcTag, exported_cc) == @as(CcTag, c_cc) and exp.opts.name.eqlSlice("main", ip) and comp.config.link_libc) {
zcu.stage1_flags.have_c_main = true;
- } else if (exported_cc == winapi_cc and target.os.tag == .windows) {
+ } else if (@as(CcTag, exported_cc) == @as(CcTag, winapi_cc) and target.os.tag == .windows) {
if (exp.opts.name.eqlSlice("WinMain", ip)) {
zcu.stage1_flags.have_winmain = true;
} else if (exp.opts.name.eqlSlice("wWinMain", ip)) {
src/link/Dwarf.zig
@@ -3398,21 +3398,68 @@ fn updateType(
const is_nullary = func_type.param_types.len == 0 and !func_type.is_var_args;
try wip_nav.abbrevCode(if (is_nullary) .nullary_func_type else .func_type);
try wip_nav.strp(name);
- try diw.writeByte(@intFromEnum(@as(DW.CC, switch (func_type.cc) {
- .Unspecified, .C => .normal,
- .Naked, .Async, .Inline => .nocall,
- .Interrupt, .Signal => .nocall,
- .Stdcall => .BORLAND_stdcall,
- .Fastcall => .BORLAND_fastcall,
- .Vectorcall => .LLVM_vectorcall,
- .Thiscall => .BORLAND_thiscall,
- .APCS => .nocall,
- .AAPCS => .LLVM_AAPCS,
- .AAPCSVFP => .LLVM_AAPCS_VFP,
- .SysV => .LLVM_X86_64SysV,
- .Win64 => .LLVM_Win64,
- .Kernel, .Fragment, .Vertex => .nocall,
- })));
+ const cc: DW.CC = cc: {
+ if (zcu.getTarget().defaultCCallingConvention()) |cc| {
+ if (@as(std.builtin.NewCallingConvention.Tag, cc) == func_type.cc) {
+ break :cc .normal;
+ }
+ }
+ break :cc switch (func_type.cc) {
+ .@"inline" => unreachable,
+ .@"async", .auto, .naked => .normal,
+ .x86_64_sysv => .LLVM_X86_64SysV,
+ .x86_64_win => .LLVM_Win64,
+ .x86_64_regcall_v3_sysv => .LLVM_X86RegCall,
+ .x86_64_regcall_v4_win => .LLVM_X86RegCall,
+ .x86_64_vectorcall => .LLVM_vectorcall,
+ .x86_sysv => .nocall,
+ .x86_win => .nocall,
+ .x86_stdcall => .BORLAND_stdcall,
+ .x86_fastcall => .BORLAND_msfastcall,
+ .x86_thiscall => .BORLAND_thiscall,
+ .x86_thiscall_mingw => .BORLAND_thiscall,
+ .x86_regcall_v3 => .LLVM_X86RegCall,
+ .x86_regcall_v4_win => .LLVM_X86RegCall,
+ .x86_vectorcall => .LLVM_vectorcall,
+
+ .aarch64_aapcs => .LLVM_AAPCS,
+ .aarch64_aapcs_darwin => .LLVM_AAPCS,
+ .aarch64_aapcs_win => .LLVM_AAPCS,
+ .aarch64_vfabi => .LLVM_AAPCS,
+ .aarch64_vfabi_sve => .LLVM_AAPCS,
+
+ .arm_apcs => .nocall,
+ .arm_aapcs => .LLVM_AAPCS,
+ .arm_aapcs_vfp => .LLVM_AAPCS_VFP,
+ .arm_aapcs16_vfp => .nocall,
+
+ .riscv64_lp64_v,
+ .riscv32_ilp32_v,
+ => .LLVM_RISCVVectorCall,
+
+ .m68k_rtd => .LLVM_M68kRTD,
+
+ .amdgcn_kernel,
+ .nvptx_kernel,
+ .spirv_kernel,
+ => .LLVM_OpenCLKernel,
+
+ .x86_64_interrupt,
+ .x86_interrupt,
+ .arm_interrupt,
+ .mips64_interrupt,
+ .mips_interrupt,
+ .riscv64_interrupt,
+ .riscv32_interrupt,
+ .avr_interrupt,
+ .csky_interrupt,
+ .m68k_interrupt,
+ => .normal,
+
+ else => .nocall,
+ };
+ };
+ try diw.writeByte(@intFromEnum(cc));
try wip_nav.refType(Type.fromInterned(func_type.return_type));
for (0..func_type.param_types.len) |param_index| {
try wip_nav.abbrevCode(.func_type_param);
src/link/SpirV.zig
@@ -165,10 +165,9 @@ pub fn updateExports(
const target = zcu.getTarget();
const spv_decl_index = try self.object.resolveNav(zcu, nav_index);
const execution_model = switch (Type.fromInterned(nav_ty).fnCallingConvention(zcu)) {
- .Vertex => spec.ExecutionModel.Vertex,
- .Fragment => spec.ExecutionModel.Fragment,
- .Kernel => spec.ExecutionModel.Kernel,
- .C => return, // TODO: What to do here?
+ .spirv_vertex => spec.ExecutionModel.Vertex,
+ .spirv_fragment => spec.ExecutionModel.Fragment,
+ .spirv_kernel => spec.ExecutionModel.Kernel,
else => unreachable,
};
const is_vulkan = target.os.tag == .vulkan;
src/Zcu/PerThread.zig
@@ -2090,7 +2090,7 @@ fn analyzeFnBody(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaError!
.code = zir,
.owner = anal_unit,
.func_index = func_index,
- .func_is_naked = fn_ty_info.cc == .Naked,
+ .func_is_naked = fn_ty_info.cc == .naked,
.fn_ret_ty = Type.fromInterned(fn_ty_info.return_type),
.fn_ret_ty_ies = null,
.branch_quota = @max(func.branchQuotaUnordered(ip), Sema.default_branch_quota),
src/InternPool.zig
@@ -1988,7 +1988,7 @@ pub const Key = union(enum) {
/// Tells whether a parameter is noalias. See `paramIsNoalias` helper
/// method for accessing this.
noalias_bits: u32,
- cc: std.builtin.CallingConvention,
+ cc: std.builtin.NewCallingConvention,
is_var_args: bool,
is_generic: bool,
is_noinline: bool,
@@ -2011,10 +2011,10 @@ pub const Key = union(enum) {
a.return_type == b.return_type and
a.comptime_bits == b.comptime_bits and
a.noalias_bits == b.noalias_bits and
- a.cc == b.cc and
a.is_var_args == b.is_var_args and
a.is_generic == b.is_generic and
- a.is_noinline == b.is_noinline;
+ a.is_noinline == b.is_noinline and
+ std.meta.eql(a.cc, b.cc);
}
pub fn hash(self: FuncType, hasher: *Hash, ip: *const InternPool) void {
@@ -5444,7 +5444,7 @@ pub const Tag = enum(u8) {
flags: Flags,
pub const Flags = packed struct(u32) {
- cc: std.builtin.CallingConvention,
+ cc: PackedCallingConvention,
is_var_args: bool,
is_generic: bool,
has_comptime_bits: bool,
@@ -5453,7 +5453,7 @@ pub const Tag = enum(u8) {
cc_is_generic: bool,
section_is_generic: bool,
addrspace_is_generic: bool,
- _: u16 = 0,
+ _: u6 = 0,
};
};
@@ -6912,7 +6912,7 @@ fn extraFuncType(tid: Zcu.PerThread.Id, extra: Local.Extra, extra_index: u32) Ke
.return_type = type_function.data.return_type,
.comptime_bits = comptime_bits,
.noalias_bits = noalias_bits,
- .cc = type_function.data.flags.cc,
+ .cc = type_function.data.flags.cc.unpack(),
.is_var_args = type_function.data.flags.is_var_args,
.is_noinline = type_function.data.flags.is_noinline,
.cc_is_generic = type_function.data.flags.cc_is_generic,
@@ -8526,7 +8526,7 @@ pub const GetFuncTypeKey = struct {
comptime_bits: u32 = 0,
noalias_bits: u32 = 0,
/// `null` means generic.
- cc: ?std.builtin.CallingConvention = .Unspecified,
+ cc: ?std.builtin.NewCallingConvention = .auto,
is_var_args: bool = false,
is_generic: bool = false,
is_noinline: bool = false,
@@ -8564,7 +8564,7 @@ pub fn getFuncType(
.params_len = params_len,
.return_type = key.return_type,
.flags = .{
- .cc = key.cc orelse .Unspecified,
+ .cc = .pack(key.cc orelse .auto),
.is_var_args = key.is_var_args,
.has_comptime_bits = key.comptime_bits != 0,
.has_noalias_bits = key.noalias_bits != 0,
@@ -8668,7 +8668,7 @@ pub const GetFuncDeclKey = struct {
rbrace_line: u32,
lbrace_column: u32,
rbrace_column: u32,
- cc: ?std.builtin.CallingConvention,
+ cc: ?std.builtin.NewCallingConvention,
is_noinline: bool,
};
@@ -8733,7 +8733,7 @@ pub const GetFuncDeclIesKey = struct {
comptime_bits: u32,
bare_return_type: Index,
/// null means generic.
- cc: ?std.builtin.CallingConvention,
+ cc: ?std.builtin.NewCallingConvention,
/// null means generic.
alignment: ?Alignment,
section_is_generic: bool,
@@ -8818,7 +8818,7 @@ pub fn getFuncDeclIes(
.params_len = params_len,
.return_type = error_union_type,
.flags = .{
- .cc = key.cc orelse .Unspecified,
+ .cc = .pack(key.cc orelse .auto),
.is_var_args = key.is_var_args,
.has_comptime_bits = key.comptime_bits != 0,
.has_noalias_bits = key.noalias_bits != 0,
@@ -8948,7 +8948,7 @@ pub const GetFuncInstanceKey = struct {
comptime_args: []const Index,
noalias_bits: u32,
bare_return_type: Index,
- cc: std.builtin.CallingConvention,
+ cc: std.builtin.NewCallingConvention,
alignment: Alignment,
section: OptionalNullTerminatedString,
is_noinline: bool,
@@ -9110,7 +9110,7 @@ pub fn getFuncInstanceIes(
.params_len = params_len,
.return_type = error_union_type,
.flags = .{
- .cc = arg.cc,
+ .cc = .pack(arg.cc),
.is_var_args = false,
.has_comptime_bits = false,
.has_noalias_bits = arg.noalias_bits != 0,
@@ -12224,3 +12224,82 @@ pub fn getErrorValue(
pub fn getErrorValueIfExists(ip: *const InternPool, name: NullTerminatedString) ?Zcu.ErrorInt {
return @intFromEnum(ip.global_error_set.getErrorValueIfExists(name) orelse return null);
}
+
+const PackedCallingConvention = packed struct(u18) {
+ tag: std.builtin.NewCallingConvention.Tag,
+ /// May be ignored depending on `tag`.
+ incoming_stack_alignment: Alignment,
+ /// Interpretation depends on `tag`.
+ extra: u4,
+
+ fn pack(cc: std.builtin.NewCallingConvention) PackedCallingConvention {
+ return switch (cc) {
+ inline else => |pl, tag| switch (@TypeOf(pl)) {
+ void => .{
+ .tag = tag,
+ .incoming_stack_alignment = .none, // unused
+ .extra = 0, // unused
+ },
+ std.builtin.NewCallingConvention.CommonOptions => .{
+ .tag = tag,
+ .incoming_stack_alignment = .fromByteUnits(pl.incoming_stack_alignment orelse 0),
+ .extra = 0, // unused
+ },
+ std.builtin.NewCallingConvention.X86RegparmOptions => .{
+ .tag = tag,
+ .incoming_stack_alignment = .fromByteUnits(pl.incoming_stack_alignment orelse 0),
+ .extra = pl.register_params,
+ },
+ std.builtin.NewCallingConvention.ArmInterruptOptions => .{
+ .tag = tag,
+ .incoming_stack_alignment = .fromByteUnits(pl.incoming_stack_alignment orelse 0),
+ .extra = @intFromEnum(pl.type),
+ },
+ std.builtin.NewCallingConvention.MipsInterruptOptions => .{
+ .tag = tag,
+ .incoming_stack_alignment = .fromByteUnits(pl.incoming_stack_alignment orelse 0),
+ .extra = @intFromEnum(pl.mode),
+ },
+ std.builtin.NewCallingConvention.RiscvInterruptOptions => .{
+ .tag = tag,
+ .incoming_stack_alignment = .fromByteUnits(pl.incoming_stack_alignment orelse 0),
+ .extra = @intFromEnum(pl.level),
+ },
+ else => comptime unreachable,
+ },
+ };
+ }
+
+ fn unpack(cc: PackedCallingConvention) std.builtin.NewCallingConvention {
+ @setEvalBranchQuota(400_000);
+ return switch (cc.tag) {
+ inline else => |tag| @unionInit(
+ std.builtin.NewCallingConvention,
+ @tagName(tag),
+ switch (std.meta.FieldType(std.builtin.NewCallingConvention, tag)) {
+ void => {},
+ std.builtin.NewCallingConvention.CommonOptions => .{
+ .incoming_stack_alignment = cc.incoming_stack_alignment.toByteUnits(),
+ },
+ std.builtin.NewCallingConvention.X86RegparmOptions => .{
+ .incoming_stack_alignment = cc.incoming_stack_alignment.toByteUnits(),
+ .register_params = @intCast(cc.extra),
+ },
+ std.builtin.NewCallingConvention.ArmInterruptOptions => .{
+ .incoming_stack_alignment = cc.incoming_stack_alignment.toByteUnits(),
+ .type = @enumFromInt(cc.extra),
+ },
+ std.builtin.NewCallingConvention.MipsInterruptOptions => .{
+ .incoming_stack_alignment = cc.incoming_stack_alignment.toByteUnits(),
+ .mode = @enumFromInt(cc.extra),
+ },
+ std.builtin.NewCallingConvention.RiscvInterruptOptions => .{
+ .incoming_stack_alignment = cc.incoming_stack_alignment.toByteUnits(),
+ .level = @enumFromInt(cc.extra),
+ },
+ else => comptime unreachable,
+ },
+ ),
+ };
+ }
+};
src/Sema.zig
@@ -26,7 +26,7 @@ owner: AnalUnit,
/// in the case of an inline or comptime function call.
/// This could be `none`, a `func_decl`, or a `func_instance`.
func_index: InternPool.Index,
-/// Whether the type of func_index has a calling convention of `.Naked`.
+/// Whether the type of func_index has a calling convention of `.naked`.
func_is_naked: bool,
/// Used to restore the error return trace when returning a non-error from a function.
error_return_trace_index_on_fn_entry: Air.Inst.Ref = .none,
@@ -1355,7 +1355,7 @@ fn analyzeBodyInner(
},
.value_placeholder => unreachable, // never appears in a body
.field_parent_ptr => try sema.zirFieldParentPtr(block, extended),
- .builtin_value => try sema.zirBuiltinValue(extended),
+ .builtin_value => try sema.zirBuiltinValue(block, extended),
.inplace_arith_result_ty => try sema.zirInplaceArithResultTy(extended),
};
},
@@ -2698,6 +2698,20 @@ fn analyzeAsInt(
return try val.toUnsignedIntSema(sema.pt);
}
+fn analyzeValueAsCallconv(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ unresolved_val: Value,
+) !std.builtin.NewCallingConvention {
+ const resolved_val = try sema.resolveLazyValue(unresolved_val);
+ return resolved_val.interpret(std.builtin.NewCallingConvention, sema.pt) catch |err| switch (err) {
+ error.OutOfMemory => |e| return e,
+ error.UndefinedValue => return sema.failWithUseOfUndef(block, src),
+ error.TypeMismatch => @panic("std.builtin is corrupt"),
+ };
+}
+
/// Given a ZIR extra index which points to a list of `Zir.Inst.Capture`,
/// resolves this into a list of `InternPool.CaptureValue` allocated by `arena`.
fn getCaptures(sema: *Sema, block: *Block, type_src: LazySrcLoc, extra_index: usize, captures_len: u32) ![]InternPool.CaptureValue {
@@ -6516,8 +6530,8 @@ fn zirSetAlignStack(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Inst
}
switch (Value.fromInterned(func).typeOf(zcu).fnCallingConvention(zcu)) {
- .Naked => return sema.fail(block, src, "@setAlignStack in naked function", .{}),
- .Inline => return sema.fail(block, src, "@setAlignStack in inline function", .{}),
+ .naked => return sema.fail(block, src, "@setAlignStack in naked function", .{}),
+ .@"inline" => return sema.fail(block, src, "@setAlignStack in inline function", .{}),
else => {},
}
@@ -7554,7 +7568,7 @@ fn analyzeCall(
if (try sema.resolveValue(func)) |func_val|
if (func_val.isUndef(zcu))
return sema.failWithUseOfUndef(block, call_src);
- if (cc == .Naked) {
+ if (cc == .naked) {
const maybe_func_inst = try sema.funcDeclSrcInst(func);
const msg = msg: {
const msg = try sema.errMsg(
@@ -7587,7 +7601,7 @@ fn analyzeCall(
.async_kw => return sema.failWithUseOfAsync(block, call_src),
};
- if (modifier == .never_inline and func_ty_info.cc == .Inline) {
+ if (modifier == .never_inline and func_ty_info.cc == .@"inline") {
return sema.fail(block, call_src, "'never_inline' call of inline function", .{});
}
if (modifier == .always_inline and func_ty_info.is_noinline) {
@@ -7598,7 +7612,7 @@ fn analyzeCall(
const is_generic_call = func_ty_info.is_generic;
var is_comptime_call = block.is_comptime or modifier == .compile_time;
- var is_inline_call = is_comptime_call or modifier == .always_inline or func_ty_info.cc == .Inline;
+ var is_inline_call = is_comptime_call or modifier == .always_inline or func_ty_info.cc == .@"inline";
var comptime_reason: ?*const Block.ComptimeReason = null;
if (!is_inline_call and !is_comptime_call) {
if (try Type.fromInterned(func_ty_info.return_type).comptimeOnlySema(pt)) {
@@ -8455,7 +8469,7 @@ fn instantiateGenericCall(
}
// Similarly, if the call evaluated to a generic type we need to instead
// call it inline.
- if (func_ty_info.is_generic or func_ty_info.cc == .Inline) {
+ if (func_ty_info.is_generic or func_ty_info.cc == .@"inline") {
return error.GenericPoison;
}
@@ -9505,8 +9519,8 @@ fn zirFunc(
// If this instruction has a body, then it's a function declaration, and we decide
// the callconv based on whether it is exported. Otherwise, the callconv defaults
- // to `.Unspecified`.
- const cc: std.builtin.CallingConvention = if (has_body) cc: {
+ // to `.auto`.
+ const cc: std.builtin.NewCallingConvention = if (has_body) cc: {
const func_decl_cau = if (sema.generic_owner != .none) cau: {
const generic_owner_fn = zcu.funcInfo(sema.generic_owner);
// The generic owner definitely has a `Cau` for the corresponding function declaration.
@@ -9518,8 +9532,26 @@ fn zirFunc(
const zir_decl = sema.code.getDeclaration(decl_inst)[0];
break :exported zir_decl.flags.is_export;
};
- break :cc if (fn_is_exported) .C else .Unspecified;
- } else .Unspecified;
+ if (fn_is_exported) {
+ break :cc target.defaultCCallingConvention() orelse {
+ // This target has no default C calling convention. We sometimes trigger a similar
+ // error by trying to evaluate `std.builtin.CallingConvention.c`, so for consistency,
+ // let's eval that now and just get the transitive error. (It's guaranteed to error
+ // because it does the exact `defaultCCallingConvention` call we just did.)
+ const cc_type = try sema.getBuiltinType("CallingConvention");
+ _ = try sema.namespaceLookupVal(
+ block,
+ LazySrcLoc.unneeded,
+ cc_type.getNamespaceIndex(zcu),
+ try ip.getOrPutString(sema.gpa, pt.tid, "c", .no_embedded_nulls),
+ );
+ // The above should have errored.
+ @panic("std.builtin is corrupt");
+ };
+ } else {
+ break :cc .auto;
+ }
+ } else .auto;
return sema.funcCommon(
block,
@@ -9654,15 +9686,64 @@ fn handleExternLibName(
/// These are calling conventions that are confirmed to work with variadic functions.
/// Any calling conventions not included here are either not yet verified to work with variadic
/// functions or there are no more other calling conventions that support variadic functions.
-const calling_conventions_supporting_var_args = [_]std.builtin.CallingConvention{
- .C,
+const calling_conventions_supporting_var_args = [_]std.builtin.NewCallingConvention.Tag{
+ .x86_64_sysv,
+ .x86_64_win,
+ .x86_sysv,
+ .x86_win,
+ .aarch64_aapcs,
+ .aarch64_aapcs_darwin,
+ .aarch64_aapcs_win,
+ .aarch64_vfabi,
+ .aarch64_vfabi_sve,
+ .arm_apcs,
+ .arm_aapcs,
+ .arm_aapcs_vfp,
+ .arm_aapcs16_vfp,
+ .mips64_n64,
+ .mips64_n32,
+ .mips_o32,
+ .riscv64_lp64,
+ .riscv64_lp64_v,
+ .riscv32_ilp32,
+ .riscv32_ilp32_v,
+ .sparc64_sysv,
+ .sparc_sysv,
+ .powerpc64_elf,
+ .powerpc64_elf_altivec,
+ .powerpc64_elf_v2,
+ .powerpc_sysv,
+ .powerpc_sysv_altivec,
+ .powerpc_aix,
+ .powerpc_aix_altivec,
+ .wasm_watc,
+ .arc_sysv,
+ .avr_gnu,
+ .bpf_std,
+ .csky_sysv,
+ .hexagon_sysv,
+ .hexagon_sysv_hvx,
+ .lanai_sysv,
+ .loongarch64_lp64,
+ .loongarch32_ilp32,
+ .m68k_sysv,
+ .m68k_gnu,
+ .m68k_rtd,
+ .msp430_eabi,
+ .s390x_sysv,
+ .s390x_sysv_vx,
+ .ve_sysv,
+ .xcore_xs1,
+ .xcore_xs2,
+ .xtensa_call0,
+ .xtensa_windowed,
};
-fn callConvSupportsVarArgs(cc: std.builtin.CallingConvention) bool {
+fn callConvSupportsVarArgs(cc: std.builtin.NewCallingConvention.Tag) bool {
return for (calling_conventions_supporting_var_args) |supported_cc| {
if (cc == supported_cc) return true;
} else false;
}
-fn checkCallConvSupportsVarArgs(sema: *Sema, block: *Block, src: LazySrcLoc, cc: std.builtin.CallingConvention) CompileError!void {
+fn checkCallConvSupportsVarArgs(sema: *Sema, block: *Block, src: LazySrcLoc, cc: std.builtin.NewCallingConvention.Tag) CompileError!void {
const CallingConventionsSupportingVarArgsList = struct {
pub fn format(_: @This(), comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
_ = fmt;
@@ -9703,7 +9784,7 @@ fn funcCommon(
address_space: ?std.builtin.AddressSpace,
section: Section,
/// null means generic poison
- cc: ?std.builtin.CallingConvention,
+ cc: ?std.builtin.NewCallingConvention,
/// this might be Type.generic_poison
bare_return_type: Type,
var_args: bool,
@@ -9743,7 +9824,7 @@ fn funcCommon(
// default values which are only meaningful for the generic function, *not*
// the instantiation, which can depend on comptime parameters.
// Related proposal: https://github.com/ziglang/zig/issues/11834
- const cc_resolved = cc orelse .Unspecified;
+ const cc_resolved = cc orelse .auto;
var comptime_bits: u32 = 0;
for (block.params.items(.ty), block.params.items(.is_comptime), 0..) |param_ty_ip, param_is_comptime, i| {
const param_ty = Type.fromInterned(param_ty_ip);
@@ -9761,10 +9842,10 @@ fn funcCommon(
}
const this_generic = param_ty.isGenericPoison();
is_generic = is_generic or this_generic;
- if (param_is_comptime and !target_util.fnCallConvAllowsZigTypes(target, cc_resolved)) {
+ if (param_is_comptime and !target_util.fnCallConvAllowsZigTypes(cc_resolved)) {
return sema.fail(block, param_src, "comptime parameters not allowed in function with calling convention '{s}'", .{@tagName(cc_resolved)});
}
- if (this_generic and !sema.no_partial_func_ty and !target_util.fnCallConvAllowsZigTypes(target, cc_resolved)) {
+ if (this_generic and !sema.no_partial_func_ty and !target_util.fnCallConvAllowsZigTypes(cc_resolved)) {
return sema.fail(block, param_src, "generic parameters not allowed in function with calling convention '{s}'", .{@tagName(cc_resolved)});
}
if (!param_ty.isValidParamType(zcu)) {
@@ -9773,7 +9854,7 @@ fn funcCommon(
opaque_str, param_ty.fmt(pt),
});
}
- if (!this_generic and !target_util.fnCallConvAllowsZigTypes(target, cc_resolved) and !try sema.validateExternType(param_ty, .param_ty)) {
+ if (!this_generic and !target_util.fnCallConvAllowsZigTypes(cc_resolved) and !try sema.validateExternType(param_ty, .param_ty)) {
const msg = msg: {
const msg = try sema.errMsg(param_src, "parameter of type '{}' not allowed in function with calling convention '{s}'", .{
param_ty.fmt(pt), @tagName(cc_resolved),
@@ -9807,15 +9888,24 @@ fn funcCommon(
return sema.fail(block, param_src, "non-pointer parameter declared noalias", .{});
}
switch (cc_resolved) {
- .Interrupt => if (target.cpu.arch.isX86()) {
+ .x86_64_interrupt, .x86_interrupt => {
const err_code_size = target.ptrBitWidth();
switch (i) {
0 => if (param_ty.zigTypeTag(zcu) != .pointer) return sema.fail(block, param_src, "first parameter of function with 'Interrupt' calling convention must be a pointer type", .{}),
1 => if (param_ty.bitSize(zcu) != err_code_size) return sema.fail(block, param_src, "second parameter of function with 'Interrupt' calling convention must be a {d}-bit integer", .{err_code_size}),
else => return sema.fail(block, param_src, "'Interrupt' calling convention supports up to 2 parameters, found {d}", .{i + 1}),
}
- } else return sema.fail(block, param_src, "parameters are not allowed with 'Interrupt' calling convention", .{}),
- .Signal => return sema.fail(block, param_src, "parameters are not allowed with 'Signal' calling convention", .{}),
+ },
+ .arm_interrupt,
+ .mips64_interrupt,
+ .mips_interrupt,
+ .riscv64_interrupt,
+ .riscv32_interrupt,
+ .avr_interrupt,
+ .csky_interrupt,
+ .m68k_interrupt,
+ => return sema.fail(block, param_src, "parameters are not allowed with 'Interrupt' calling convention", .{}),
+ .avr_signal => return sema.fail(block, param_src, "parameters are not allowed with 'Signal' calling convention", .{}),
else => {},
}
}
@@ -10051,7 +10141,7 @@ fn finishFunc(
ret_poison: bool,
bare_return_type: Type,
ret_ty_src: LazySrcLoc,
- cc_resolved: std.builtin.CallingConvention,
+ cc_resolved: std.builtin.NewCallingConvention,
is_source_decl: bool,
ret_ty_requires_comptime: bool,
func_inst: Zir.Inst.Index,
@@ -10064,7 +10154,6 @@ fn finishFunc(
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
const gpa = sema.gpa;
- const target = zcu.getTarget();
const return_type: Type = if (opt_func_index == .none or ret_poison)
bare_return_type
@@ -10077,7 +10166,7 @@ fn finishFunc(
opaque_str, return_type.fmt(pt),
});
}
- if (!ret_poison and !target_util.fnCallConvAllowsZigTypes(target, cc_resolved) and
+ if (!ret_poison and !target_util.fnCallConvAllowsZigTypes(cc_resolved) and
!try sema.validateExternType(return_type, .ret_ty))
{
const msg = msg: {
@@ -10134,56 +10223,50 @@ fn finishFunc(
}
switch (cc_resolved) {
- .Interrupt, .Signal => if (return_type.zigTypeTag(zcu) != .void and return_type.zigTypeTag(zcu) != .noreturn) {
+ .x86_64_interrupt,
+ .x86_interrupt,
+ .arm_interrupt,
+ .mips64_interrupt,
+ .mips_interrupt,
+ .riscv64_interrupt,
+ .riscv32_interrupt,
+ .avr_interrupt,
+ .csky_interrupt,
+ .m68k_interrupt,
+ .avr_signal,
+ => if (return_type.zigTypeTag(zcu) != .void and return_type.zigTypeTag(zcu) != .noreturn) {
return sema.fail(block, ret_ty_src, "function with calling convention '{s}' must return 'void' or 'noreturn'", .{@tagName(cc_resolved)});
},
- .Inline => if (is_noinline) {
- return sema.fail(block, cc_src, "'noinline' function cannot have callconv 'Inline'", .{});
+ .@"inline" => if (is_noinline) {
+ return sema.fail(block, cc_src, "'noinline' function cannot have callconv 'inline'", .{});
},
else => {},
}
- const arch = target.cpu.arch;
- if (@as(?[]const u8, switch (cc_resolved) {
- .Unspecified, .C, .Naked, .Async, .Inline => null,
- .Interrupt => switch (arch) {
- .x86, .x86_64, .avr, .msp430 => null,
- else => "x86, x86_64, AVR, and MSP430",
- },
- .Signal => switch (arch) {
- .avr => null,
- else => "AVR",
- },
- .Stdcall, .Fastcall, .Thiscall => switch (arch) {
- .x86 => null,
- else => "x86",
- },
- .Vectorcall => switch (arch) {
- .x86, .aarch64, .aarch64_be => null,
- else => "x86 and AArch64",
- },
- .APCS, .AAPCS, .AAPCSVFP => switch (arch) {
- .arm, .armeb, .aarch64, .aarch64_be, .thumb, .thumbeb => null,
- else => "ARM",
- },
- .SysV, .Win64 => switch (arch) {
- .x86_64 => null,
- else => "x86_64",
- },
- .Kernel => switch (arch) {
- .nvptx, .nvptx64, .amdgcn, .spirv, .spirv32, .spirv64 => null,
- else => "nvptx, amdgcn and SPIR-V",
- },
- .Fragment, .Vertex => switch (arch) {
- .spirv, .spirv32, .spirv64 => null,
- else => "SPIR-V",
- },
- })) |allowed_platform| {
- return sema.fail(block, cc_src, "callconv '{s}' is only available on {s}, not {s}", .{
+ switch (zcu.callconvSupported(cc_resolved)) {
+ .ok => {},
+ .bad_arch => |allowed_archs| {
+ const ArchListFormatter = struct {
+ archs: []const std.Target.Cpu.Arch,
+ pub fn format(formatter: @This(), comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
+ _ = fmt;
+ _ = options;
+ for (formatter.archs, 0..) |arch, i| {
+ if (i != 0)
+ try writer.writeAll(", ");
+ try writer.print("'.{s}'", .{@tagName(arch)});
+ }
+ }
+ };
+ return sema.fail(block, cc_src, "callconv '{s}' only available on architectures {}", .{
+ @tagName(cc_resolved),
+ ArchListFormatter{ .archs = allowed_archs },
+ });
+ },
+ .bad_backend => |bad_backend| return sema.fail(block, cc_src, "callconv '{s}' not supported by compiler backend '{s}'", .{
@tagName(cc_resolved),
- allowed_platform,
- @tagName(arch),
- });
+ @tagName(bad_backend),
+ }),
}
if (is_generic and sema.no_partial_func_ty) return error.GenericPoison;
@@ -18342,10 +18425,14 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
} });
const callconv_ty = try sema.getBuiltinType("CallingConvention");
+ const callconv_val = Value.uninterpret(func_ty_info.cc, callconv_ty, pt) catch |err| switch (err) {
+ error.TypeMismatch => @panic("std.builtin is corrupt"),
+ error.OutOfMemory => |e| return e,
+ };
- const field_values = .{
+ const field_values: [5]InternPool.Index = .{
// calling_convention: CallingConvention,
- (try pt.enumValueFieldIndex(callconv_ty, @intFromEnum(func_ty_info.cc))).toIntern(),
+ callconv_val.toIntern(),
// is_generic: bool,
Value.makeBool(func_ty_info.is_generic).toIntern(),
// is_var_args: bool,
@@ -22171,7 +22258,7 @@ fn zirReify(
}
const is_var_args = is_var_args_val.toBool();
- const cc = zcu.toEnum(std.builtin.CallingConvention, calling_convention_val);
+ const cc = try sema.analyzeValueAsCallconv(block, src, calling_convention_val);
if (is_var_args) {
try sema.checkCallConvSupportsVarArgs(block, src, cc);
}
@@ -26657,7 +26744,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
break :blk .{ .explicit = section_name };
} else .default;
- const cc: ?std.builtin.CallingConvention = if (extra.data.bits.has_cc_body) blk: {
+ const cc: ?std.builtin.NewCallingConvention = if (extra.data.bits.has_cc_body) blk: {
const body_len = sema.code.extra[extra_index];
extra_index += 1;
const body = sema.code.bodySlice(extra_index, body_len);
@@ -26670,7 +26757,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
if (val.isGenericPoison()) {
break :blk null;
}
- break :blk zcu.toEnum(std.builtin.CallingConvention, val);
+ break :blk try sema.analyzeValueAsCallconv(block, cc_src, val);
} else if (extra.data.bits.has_cc_ref) blk: {
const cc_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
extra_index += 1;
@@ -26689,7 +26776,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
error.GenericPoison => break :blk null,
else => |e| return e,
};
- break :blk zcu.toEnum(std.builtin.CallingConvention, cc_val);
+ break :blk try sema.analyzeValueAsCallconv(block, cc_src, cc_val);
} else cc: {
if (has_body) {
const decl_inst = if (sema.generic_owner != .none) decl_inst: {
@@ -26705,7 +26792,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
break :cc .C;
}
}
- break :cc .Unspecified;
+ break :cc .auto;
};
const ret_ty: Type = if (extra.data.bits.has_ret_ty_body) blk: {
@@ -27132,9 +27219,15 @@ fn zirInComptime(
return if (block.is_comptime) .bool_true else .bool_false;
}
-fn zirBuiltinValue(sema: *Sema, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
+fn zirBuiltinValue(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
const pt = sema.pt;
+ const zcu = pt.zcu;
+ const gpa = zcu.gpa;
+ const ip = &zcu.intern_pool;
+
+ const src = block.nodeOffset(@bitCast(extended.operand));
const value: Zir.Inst.BuiltinValue = @enumFromInt(extended.small);
+
const type_name = switch (value) {
.atomic_order => "AtomicOrder",
.atomic_rmw_op => "AtomicRmwOp",
@@ -27152,21 +27245,25 @@ fn zirBuiltinValue(sema: *Sema, extended: Zir.Inst.Extended.InstData) CompileErr
// Values are handled here.
.calling_convention_c => {
const callconv_ty = try sema.getBuiltinType("CallingConvention");
- comptime assert(@intFromEnum(std.builtin.CallingConvention.C) == 1);
- const val = try pt.intern(.{ .enum_tag = .{
- .ty = callconv_ty.toIntern(),
- .int = .one_u8,
- } });
- return Air.internedToRef(val);
+ return try sema.namespaceLookupVal(
+ block,
+ src,
+ callconv_ty.getNamespaceIndex(zcu),
+ try ip.getOrPutString(gpa, pt.tid, "c", .no_embedded_nulls),
+ ) orelse @panic("std.builtin is corrupt");
},
.calling_convention_inline => {
+ comptime assert(@typeInfo(std.builtin.NewCallingConvention.Tag).@"enum".tag_type == u8);
const callconv_ty = try sema.getBuiltinType("CallingConvention");
- comptime assert(@intFromEnum(std.builtin.CallingConvention.Inline) == 4);
- const val = try pt.intern(.{ .enum_tag = .{
- .ty = callconv_ty.toIntern(),
- .int = .four_u8,
- } });
- return Air.internedToRef(val);
+ const callconv_tag_ty = callconv_ty.unionTagType(zcu) orelse @panic("std.builtin is corrupt");
+ const inline_tag_val = try pt.enumValue(
+ callconv_tag_ty,
+ (try pt.intValue(
+ Type.u8,
+ @intFromEnum(std.builtin.NewCallingConvention.@"inline"),
+ )).toIntern(),
+ );
+ return sema.coerce(block, callconv_ty, Air.internedToRef(inline_tag_val.toIntern()), src);
},
};
const ty = try sema.getBuiltinType(type_name);
@@ -27353,7 +27450,7 @@ fn explainWhyTypeIsComptimeInner(
try sema.errNote(src_loc, msg, "function is generic", .{});
}
switch (fn_info.cc) {
- .Inline => try sema.errNote(src_loc, msg, "function has inline calling convention", .{}),
+ .@"inline" => try sema.errNote(src_loc, msg, "function has inline calling convention", .{}),
else => {},
}
if (Type.fromInterned(fn_info.return_type).comptimeOnly(zcu)) {
@@ -27461,13 +27558,12 @@ fn validateExternType(
},
.@"fn" => {
if (position != .other) return false;
- const target = zcu.getTarget();
// For now we want to authorize PTX kernel to use zig objects, even if we end up exposing the ABI.
// The goal is to experiment with more integrated CPU/GPU code.
- if (ty.fnCallingConvention(zcu) == .Kernel and (target.cpu.arch == .nvptx or target.cpu.arch == .nvptx64)) {
+ if (ty.fnCallingConvention(zcu) == .nvptx_kernel) {
return true;
}
- return !target_util.fnCallConvAllowsZigTypes(target, ty.fnCallingConvention(zcu));
+ return !target_util.fnCallConvAllowsZigTypes(ty.fnCallingConvention(zcu));
},
.@"enum" => {
return sema.validateExternType(ty.intTagType(zcu), position);
@@ -27547,9 +27643,9 @@ fn explainWhyTypeIsNotExtern(
return;
}
switch (ty.fnCallingConvention(zcu)) {
- .Unspecified => try sema.errNote(src_loc, msg, "extern function must specify calling convention", .{}),
- .Async => try sema.errNote(src_loc, msg, "async function cannot be extern", .{}),
- .Inline => try sema.errNote(src_loc, msg, "inline function cannot be extern", .{}),
+ .auto => try sema.errNote(src_loc, msg, "extern function must specify calling convention", .{}),
+ .@"async" => try sema.errNote(src_loc, msg, "async function cannot be extern", .{}),
+ .@"inline" => try sema.errNote(src_loc, msg, "inline function cannot be extern", .{}),
else => return,
}
},
@@ -30525,8 +30621,8 @@ const InMemoryCoercionResult = union(enum) {
};
const CC = struct {
- actual: std.builtin.CallingConvention,
- wanted: std.builtin.CallingConvention,
+ actual: std.builtin.NewCallingConvention,
+ wanted: std.builtin.NewCallingConvention,
};
const BitRange = struct {
@@ -31176,8 +31272,8 @@ fn coerceInMemoryAllowedFns(
return InMemoryCoercionResult{ .fn_generic = dest_info.is_generic };
}
- if (dest_info.cc != src_info.cc) {
- return InMemoryCoercionResult{ .fn_cc = .{
+ if (!callconvCoerceAllowed(target, src_info.cc, dest_info.cc)) {
+ return .{ .fn_cc = .{
.actual = src_info.cc,
.wanted = dest_info.cc,
} };
@@ -31250,6 +31346,44 @@ fn coerceInMemoryAllowedFns(
return .ok;
}
+fn callconvCoerceAllowed(
+ target: std.Target,
+ src_cc: std.builtin.NewCallingConvention,
+ dest_cc: std.builtin.NewCallingConvention,
+) bool {
+ const Tag = std.builtin.NewCallingConvention.Tag;
+ if (@as(Tag, src_cc) != @as(Tag, dest_cc)) return false;
+
+ switch (src_cc) {
+ inline else => |src_data, tag| {
+ const dest_data = @field(dest_cc, @tagName(tag));
+ if (@TypeOf(src_data) != void) {
+ const default_stack_align = target.stackAlignment();
+ const src_stack_align = src_data.incoming_stack_alignment orelse default_stack_align;
+ const dest_stack_align = src_data.incoming_stack_alignment orelse default_stack_align;
+ if (dest_stack_align < src_stack_align) return false;
+ }
+ switch (@TypeOf(src_data)) {
+ void, std.builtin.NewCallingConvention.CommonOptions => {},
+ std.builtin.NewCallingConvention.X86RegparmOptions => {
+ if (src_data.register_params != dest_data.register_params) return false;
+ },
+ std.builtin.NewCallingConvention.ArmInterruptOptions => {
+ if (src_data.type != dest_data.type) return false;
+ },
+ std.builtin.NewCallingConvention.MipsInterruptOptions => {
+ if (src_data.mode != dest_data.mode) return false;
+ },
+ std.builtin.NewCallingConvention.RiscvInterruptOptions => {
+ if (src_data.level != dest_data.level) return false;
+ },
+ else => comptime unreachable,
+ }
+ },
+ }
+ return true;
+}
+
fn coerceInMemoryAllowedPtrs(
sema: *Sema,
block: *Block,
@@ -36306,7 +36440,7 @@ fn resolveInferredErrorSet(
// because inline function does not create a new declaration, and the ies has been filled with analyzeCall,
// so here we can simply skip this case.
if (ies_func_info.return_type == .generic_poison_type) {
- assert(ies_func_info.cc == .Inline);
+ assert(ies_func_info.cc == .@"inline");
} else if (ip.errorUnionSet(ies_func_info.return_type) == ies_index) {
if (ies_func_info.is_generic) {
return sema.failWithOwnedErrorMsg(block, msg: {
src/target.zig
@@ -544,13 +544,13 @@ pub fn compilerRtIntAbbrev(bits: u16) []const u8 {
};
}
-pub fn fnCallConvAllowsZigTypes(target: std.Target, cc: std.builtin.CallingConvention) bool {
+pub fn fnCallConvAllowsZigTypes(cc: std.builtin.NewCallingConvention) bool {
return switch (cc) {
- .Unspecified, .Async, .Inline => true,
+ .auto, .@"async", .@"inline" => true,
// For now we want to authorize PTX kernel to use zig objects, even if
// we end up exposing the ABI. The goal is to experiment with more
// integrated CPU/GPU code.
- .Kernel => target.cpu.arch == .nvptx or target.cpu.arch == .nvptx64,
+ .nvptx_kernel => true,
else => false,
};
}
src/translate_c.zig
@@ -4,7 +4,6 @@ const assert = std.debug.assert;
const mem = std.mem;
const math = std.math;
const meta = std.meta;
-const CallingConvention = std.builtin.CallingConvention;
const clang = @import("clang.zig");
const aro = @import("aro");
const CToken = aro.Tokenizer.Token;
@@ -5001,17 +5000,18 @@ fn transCC(
c: *Context,
fn_ty: *const clang.FunctionType,
source_loc: clang.SourceLocation,
-) !CallingConvention {
+) !ast.Payload.Func.CallingConvention {
const clang_cc = fn_ty.getCallConv();
- switch (clang_cc) {
- .C => return CallingConvention.C,
- .X86StdCall => return CallingConvention.Stdcall,
- .X86FastCall => return CallingConvention.Fastcall,
- .X86VectorCall, .AArch64VectorCall => return CallingConvention.Vectorcall,
- .X86ThisCall => return CallingConvention.Thiscall,
- .AAPCS => return CallingConvention.AAPCS,
- .AAPCS_VFP => return CallingConvention.AAPCSVFP,
- .X86_64SysV => return CallingConvention.SysV,
+ return switch (clang_cc) {
+ .C => .c,
+ .X86_64SysV => .x86_64_sysv,
+ .X86StdCall => .x86_stdcall,
+ .X86FastCall => .x86_fastcall,
+ .X86ThisCall => .x86_thiscall,
+ .X86VectorCall => .x86_vectorcall,
+ .AArch64VectorCall => .aarch64_vfabi,
+ .AAPCS => .arm_aapcs,
+ .AAPCS_VFP => .arm_aapcs_vfp,
else => return fail(
c,
error.UnsupportedType,
@@ -5019,7 +5019,7 @@ fn transCC(
"unsupported calling convention: {s}",
.{@tagName(clang_cc)},
),
- }
+ };
}
fn transFnProto(
@@ -5056,7 +5056,7 @@ fn finishTransFnProto(
source_loc: clang.SourceLocation,
fn_decl_context: ?FnDeclContext,
is_var_args: bool,
- cc: CallingConvention,
+ cc: ast.Payload.Func.CallingConvention,
is_pub: bool,
) !*ast.Payload.Func {
const is_export = if (fn_decl_context) |ctx| ctx.is_export else false;
@@ -5104,7 +5104,7 @@ fn finishTransFnProto(
const alignment = if (fn_decl) |decl| ClangAlignment.forFunc(c, decl).zigAlignment() else null;
- const explicit_callconv = if ((is_inline or is_export or is_extern) and cc == .C) null else cc;
+ const explicit_callconv = if ((is_inline or is_export or is_extern) and cc == .c) null else cc;
const return_type_node = blk: {
if (fn_ty.getNoReturnAttr()) {
src/Type.zig
@@ -390,10 +390,14 @@ pub fn print(ty: Type, writer: anytype, pt: Zcu.PerThread) @TypeOf(writer).Error
try writer.writeAll("...");
}
try writer.writeAll(") ");
- if (fn_info.cc != .Unspecified) {
- try writer.writeAll("callconv(.");
- try writer.writeAll(@tagName(fn_info.cc));
- try writer.writeAll(") ");
+ if (fn_info.cc != .auto) print_cc: {
+ if (zcu.getTarget().defaultCCallingConvention()) |ccc| {
+ if (fn_info.cc.eql(ccc)) {
+ try writer.writeAll("callconv(.c) ");
+ break :print_cc;
+ }
+ }
+ try writer.print("callconv({any}) ", .{fn_info.cc});
}
if (fn_info.return_type == .generic_poison_type) {
try writer.writeAll("anytype");
@@ -791,7 +795,7 @@ pub fn fnHasRuntimeBitsInner(
const fn_info = zcu.typeToFunc(ty).?;
if (fn_info.is_generic) return false;
if (fn_info.is_var_args) return true;
- if (fn_info.cc == .Inline) return false;
+ if (fn_info.cc == .@"inline") return false;
return !try Type.fromInterned(fn_info.return_type).comptimeOnlyInner(strat, zcu, tid);
}
@@ -2489,7 +2493,7 @@ pub fn fnReturnType(ty: Type, zcu: *const Zcu) Type {
}
/// Asserts the type is a function.
-pub fn fnCallingConvention(ty: Type, zcu: *const Zcu) std.builtin.CallingConvention {
+pub fn fnCallingConvention(ty: Type, zcu: *const Zcu) std.builtin.NewCallingConvention {
return zcu.intern_pool.indexToKey(ty.toIntern()).func_type.cc;
}
src/Value.zig
@@ -4490,3 +4490,163 @@ pub fn resolveLazy(
else => return val,
}
}
+
+/// Given a `Value` representing a comptime-known value of type `T`, unwrap it into an actual `T` known to the compiler.
+/// This is useful for accessing `std.builtin` structures received from comptime logic.
+/// `val` must be fully resolved.
+pub fn interpret(val: Value, comptime T: type, pt: Zcu.PerThread) error{ OutOfMemory, UndefinedValue, TypeMismatch }!T {
+ @setEvalBranchQuota(400_000);
+
+ const zcu = pt.zcu;
+ const ip = &zcu.intern_pool;
+ const ty = val.typeOf(zcu);
+ if (ty.zigTypeTag(zcu) != @typeInfo(T)) return error.TypeMismatch;
+ if (val.isUndef(zcu)) return error.UndefinedValue;
+
+ return switch (@typeInfo(T)) {
+ .type,
+ .noreturn,
+ .comptime_float,
+ .comptime_int,
+ .undefined,
+ .null,
+ .@"fn",
+ .@"opaque",
+ .enum_literal,
+ => comptime unreachable, // comptime-only or otherwise impossible
+
+ .pointer,
+ .array,
+ .error_union,
+ .error_set,
+ .frame,
+ .@"anyframe",
+ .vector,
+ => comptime unreachable, // unsupported
+
+ .void => {},
+
+ .bool => switch (val.toIntern()) {
+ .bool_false => false,
+ .bool_true => true,
+ else => unreachable,
+ },
+
+ .int => switch (ip.indexToKey(val.toIntern()).int.storage) {
+ .lazy_align, .lazy_size => unreachable, // `val` is fully resolved
+ inline .u64, .i64 => |x| std.math.cast(T, x) orelse return error.TypeMismatch,
+ .big_int => |big| big.to(T) catch return error.TypeMismatch,
+ },
+
+ .float => val.toFloat(T, zcu),
+
+ .optional => |opt| if (val.optionalValue(zcu)) |unwrapped|
+ try unwrapped.interpret(opt.child, pt)
+ else
+ null,
+
+ .@"enum" => zcu.toEnum(T, val),
+
+ .@"union" => |@"union"| {
+ const union_obj = zcu.typeToUnion(ty) orelse return error.TypeMismatch;
+ if (union_obj.field_types.len != @"union".fields.len) return error.TypeMismatch;
+ const tag_val = val.unionTag(zcu) orelse return error.TypeMismatch;
+ const tag = try tag_val.interpret(@"union".tag_type.?, pt);
+ switch (tag) {
+ inline else => |tag_comptime| {
+ const Payload = std.meta.FieldType(T, tag_comptime);
+ const payload = try val.unionValue(zcu).interpret(Payload, pt);
+ return @unionInit(T, @tagName(tag_comptime), payload);
+ },
+ }
+ },
+
+ .@"struct" => |@"struct"| {
+ if (ty.structFieldCount(zcu) != @"struct".fields.len) return error.TypeMismatch;
+ var result: T = undefined;
+ inline for (@"struct".fields, 0..) |field, field_idx| {
+ const field_val = try val.fieldValue(pt, field_idx);
+ @field(result, field.name) = try field_val.interpret(field.type, pt);
+ }
+ return result;
+ },
+ };
+}
+
+/// Given any `val` and a `Type` corresponding `@TypeOf(val)`, construct a `Value` representing it which can be used
+/// within the compilation. This is useful for passing `std.builtin` structures in the compiler back to the compilation.
+/// This is the inverse of `interpret`.
+pub fn uninterpret(val: anytype, ty: Type, pt: Zcu.PerThread) error{ OutOfMemory, TypeMismatch }!Value {
+ @setEvalBranchQuota(400_000);
+
+ const T = @TypeOf(val);
+
+ const zcu = pt.zcu;
+ if (ty.zigTypeTag(zcu) != @typeInfo(T)) return error.TypeMismatch;
+
+ return switch (@typeInfo(T)) {
+ .type,
+ .noreturn,
+ .comptime_float,
+ .comptime_int,
+ .undefined,
+ .null,
+ .@"fn",
+ .@"opaque",
+ .enum_literal,
+ => comptime unreachable, // comptime-only or otherwise impossible
+
+ .pointer,
+ .array,
+ .error_union,
+ .error_set,
+ .frame,
+ .@"anyframe",
+ .vector,
+ => comptime unreachable, // unsupported
+
+ .void => .void,
+
+ .bool => if (val) .true else .false,
+
+ .int => try pt.intValue(ty, val),
+
+ .float => try pt.floatValue(ty, val),
+
+ .optional => if (val) |some|
+ .fromInterned(try pt.intern(.{ .opt = .{
+ .ty = ty.toIntern(),
+ .val = (try uninterpret(some, ty.optionalChild(zcu), pt)).toIntern(),
+ } }))
+ else
+ try pt.nullValue(ty),
+
+ .@"enum" => try pt.enumValue(ty, (try uninterpret(@intFromEnum(val), ty.intTagType(zcu), pt)).toIntern()),
+
+ .@"union" => |@"union"| {
+ const tag: @"union".tag_type.? = val;
+ const tag_val = try uninterpret(tag, ty.unionTagType(zcu).?, pt);
+ const field_ty = ty.unionFieldType(tag_val, zcu) orelse return error.TypeMismatch;
+ return switch (val) {
+ inline else => |payload| try pt.unionValue(
+ ty,
+ tag_val,
+ try uninterpret(payload, field_ty, pt),
+ ),
+ };
+ },
+
+ .@"struct" => |@"struct"| {
+ if (ty.structFieldCount(zcu) != @"struct".fields.len) return error.TypeMismatch;
+ var field_vals: [@"struct".fields.len]InternPool.Index = undefined;
+ inline for (&field_vals, @"struct".fields, 0..) |*field_val, field, field_idx| {
+ const field_ty = ty.fieldType(field_idx, zcu);
+ field_val.* = (try uninterpret(@field(val, field.name), field_ty, pt)).toIntern();
+ }
+ return .fromInterned(try pt.intern(.{ .aggregate = .{
+ .ty = ty.toIntern(),
+ .storage = .{ .elems = &field_vals },
+ } }));
+ },
+ };
+}
src/Zcu.zig
@@ -3539,3 +3539,93 @@ pub fn maybeUnresolveIes(zcu: *Zcu, func_index: InternPool.Index) !void {
zcu.intern_pool.funcSetIesResolved(func_index, .none);
}
}
+
+pub fn callconvSupported(zcu: *Zcu, cc: std.builtin.NewCallingConvention) union(enum) {
+ ok,
+ bad_arch: []const std.Target.Cpu.Arch, // value is allowed archs for cc
+ bad_backend: std.builtin.CompilerBackend, // value is current backend
+} {
+ const target = zcu.getTarget();
+ const backend = target_util.zigBackend(target, zcu.comp.config.use_llvm);
+ switch (cc) {
+ .auto, .@"inline" => return .ok,
+ .@"async" => return .{ .bad_backend = backend }, // nothing supports async currently
+ .naked => {}, // depends only on backend
+ else => for (cc.archs()) |allowed_arch| {
+ if (allowed_arch == target.cpu.arch) break;
+ } else return .{ .bad_arch = cc.archs() },
+ }
+ const backend_ok = switch (backend) {
+ .stage1 => unreachable,
+ .other => unreachable,
+ _ => unreachable,
+
+ .stage2_llvm => @import("codegen/llvm.zig").toLlvmCallConv(cc, target) != null,
+ .stage2_c => ok: {
+ if (target.defaultCCallingConvention()) |default_c| {
+ if (cc.eql(default_c)) {
+ break :ok true;
+ }
+ }
+ break :ok switch (cc) {
+ .x86_64_vectorcall,
+ .x86_fastcall,
+ .x86_thiscall,
+ .x86_vectorcall,
+ => |opts| opts.incoming_stack_alignment == null,
+
+ .x86_stdcall,
+ => |opts| opts.incoming_stack_alignment == null and opts.register_params == 0,
+
+ .naked => true,
+
+ else => false,
+ };
+ },
+ .stage2_wasm => switch (cc) {
+ .wasm_watc => |opts| opts.incoming_stack_alignment == null,
+ else => false,
+ },
+ .stage2_arm => switch (cc) {
+ .arm_aapcs => |opts| opts.incoming_stack_alignment == null,
+ .naked => true,
+ else => false,
+ },
+ .stage2_x86_64 => switch (cc) {
+ .x86_64_sysv, .x86_64_win, .naked => true,
+ else => false,
+ },
+ .stage2_aarch64 => switch (cc) {
+ .aarch64_aapcs => |opts| opts.incoming_stack_alignment == null,
+ .naked => true,
+ else => false,
+ },
+ .stage2_x86 => switch (cc) {
+ .x86_sysv,
+ .x86_win,
+ => |opts| opts.incoming_stack_alignment == null and opts.register_params == 0,
+ .naked => true,
+ else => false,
+ },
+ .stage2_riscv64 => switch (cc) {
+ .riscv64_lp64 => |opts| opts.incoming_stack_alignment == null,
+ .naked => true,
+ else => false,
+ },
+ .stage2_sparc64 => switch (cc) {
+ .sparc64_sysv => |opts| opts.incoming_stack_alignment == null,
+ .naked => true,
+ else => false,
+ },
+ .stage2_spirv64 => switch (cc) {
+ .spirv_device,
+ .spirv_kernel,
+ .spirv_fragment,
+ .spirv_vertex,
+ => true,
+ else => false,
+ },
+ };
+ if (!backend_ok) return .{ .bad_backend = backend };
+ return .ok;
+}