authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2025-01-03 01:48:42-05:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2025-01-16 20:47:30-05:00
log074232b3e53f6a6bbf0851a97c733a848e739bd8
tree8ef66708a086caf8a4bc56043ddf3ed170bcd6d1
parent3c74a478a41d2f806f3b9b2bb86fffbc40e100c3

x86_64: implement a custom calling convention for the Zig language


4 files changed, 231 insertions(+), 131 deletions(-)

src/Type.zig+11
......@@ -1920,6 +1920,17 @@ pub fn isSlice(ty: Type, zcu: *const Zcu) bool {
19201920 };
19211921}
19221922
1923pub fn isSliceAtRuntime(ty: Type, zcu: *const Zcu) bool {
1924 return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
1925 .ptr_type => |ptr_type| ptr_type.flags.size == .slice,
1926 .opt_type => |child| switch (zcu.intern_pool.indexToKey(child)) {
1927 .ptr_type => |ptr_type| !ptr_type.flags.is_allowzero and ptr_type.flags.size == .slice,
1928 else => false,
1929 },
1930 else => false,
1931 };
1932}
1933
19231934pub fn slicePtrFieldType(ty: Type, zcu: *const Zcu) Type {
19241935 return Type.fromInterned(zcu.intern_pool.slicePtrType(ty.toIntern()));
19251936}
src/arch/x86_64/CodeGen.zig+150-110
......@@ -952,7 +952,6 @@ pub fn generate(
952952 );
953953
954954 const fn_info = zcu.typeToFunc(fn_type).?;
955 const cc = abi.resolveCallingConvention(fn_info.cc, function.target.*);
956955 var call_info = function.resolveCallingConventionValues(fn_info, &.{}, .args_frame) catch |err| switch (err) {
957956 error.CodegenFail => return error.CodegenFail,
958957 else => |e| return e,
......@@ -978,7 +977,7 @@ pub fn generate(
978977 .alignment = call_info.stack_align,
979978 }),
980979 );
981 function.va_info = switch (cc) {
980 function.va_info = switch (fn_info.cc) {
982981 else => undefined,
983982 .x86_64_sysv => .{ .sysv = .{
984983 .gp_count = call_info.gp_count,
......@@ -1010,7 +1009,7 @@ pub fn generate(
10101009 .target = function.target,
10111010 .allocator = gpa,
10121011 .mir = mir,
1013 .cc = cc,
1012 .cc = fn_info.cc,
10141013 .src_loc = src_loc,
10151014 .output_mode = comp.config.output_mode,
10161015 .link_mode = comp.config.link_mode,
......@@ -1100,7 +1099,7 @@ pub fn generateLazy(
11001099 .target = function.target,
11011100 .allocator = gpa,
11021101 .mir = mir,
1103 .cc = abi.resolveCallingConvention(.auto, function.target.*),
1102 .cc = .auto,
11041103 .src_loc = src_loc,
11051104 .output_mode = comp.config.output_mode,
11061105 .link_mode = comp.config.link_mode,
......@@ -2126,8 +2125,7 @@ fn gen(self: *CodeGen) InnerError!void {
21262125 const pt = self.pt;
21272126 const zcu = pt.zcu;
21282127 const fn_info = zcu.typeToFunc(self.fn_type).?;
2129 const cc = abi.resolveCallingConvention(fn_info.cc, self.target.*);
2130 if (cc != .naked) {
2128 if (fn_info.cc != .naked) {
21312129 try self.asmRegister(.{ ._, .push }, .rbp);
21322130 try self.asmPseudoImmediate(.pseudo_cfi_adjust_cfa_offset_i_s, .s(8));
21332131 try self.asmPseudoRegisterImmediate(.pseudo_cfi_rel_offset_ri_s, .rbp, .s(0));
......@@ -2159,7 +2157,7 @@ fn gen(self: *CodeGen) InnerError!void {
21592157 else => unreachable,
21602158 }
21612159
2162 if (fn_info.is_var_args) switch (cc) {
2160 if (fn_info.is_var_args) switch (fn_info.cc) {
21632161 .x86_64_sysv => {
21642162 const info = &self.va_info.sysv;
21652163 const reg_save_area_fi = try self.allocFrameIndex(.init(.{
......@@ -2188,7 +2186,7 @@ fn gen(self: *CodeGen) InnerError!void {
21882186 self.performReloc(skip_sse_reloc);
21892187 },
21902188 .x86_64_win => return self.fail("TODO implement gen var arg function for Win64", .{}),
2191 else => unreachable,
2189 else => |cc| return self.fail("{s} does not support var args", .{@tagName(cc)}),
21922190 };
21932191
21942192 try self.asmPseudo(.pseudo_dbg_prologue_end_none);
......@@ -2214,7 +2212,7 @@ fn gen(self: *CodeGen) InnerError!void {
22142212 try self.asmPseudoRegisterImmediate(.pseudo_cfi_def_cfa_ri_s, .rsp, .s(8));
22152213 try self.asmOpOnly(.{ ._, .ret });
22162214
2217 const frame_layout = try self.computeFrameLayout(cc);
2215 const frame_layout = try self.computeFrameLayout(fn_info.cc);
22182216 const need_frame_align = frame_layout.stack_mask != std.math.maxInt(u32);
22192217 const need_stack_adjust = frame_layout.stack_adjust > 0;
22202218 const need_save_reg = frame_layout.save_reg_list.count() > 0;
......@@ -2262,12 +2260,13 @@ fn gen(self: *CodeGen) InnerError!void {
22622260 } },
22632261 });
22642262 } else {
2263 const scratch_reg = abi.getCAbiLinkerScratchReg(fn_info.cc);
22652264 self.mir_instructions.set(backpatch_stack_alloc, .{
22662265 .tag = .pseudo,
22672266 .ops = .pseudo_probe_adjust_setup_rri_s,
22682267 .data = .{ .rri = .{
22692268 .r1 = .rsp,
2270 .r2 = .rax,
2269 .r2 = scratch_reg,
22712270 .i = frame_layout.stack_adjust,
22722271 } },
22732272 });
......@@ -2276,25 +2275,35 @@ fn gen(self: *CodeGen) InnerError!void {
22762275 .ops = .pseudo_probe_adjust_loop_rr,
22772276 .data = .{ .rr = .{
22782277 .r1 = .rsp,
2279 .r2 = .rax,
2278 .r2 = scratch_reg,
22802279 } },
22812280 });
22822281 }
22832282 }
22842283 if (need_frame_align or need_stack_adjust) {
2285 self.mir_instructions.set(backpatch_stack_dealloc, .{
2286 .tag = .lea,
2287 .ops = .rm,
2288 .data = .{ .rx = .{
2289 .r1 = .rsp,
2290 .payload = try self.addExtra(Mir.Memory.encode(.{
2291 .base = .{ .reg = .rbp },
2292 .mod = .{ .rm = .{
2293 .size = .qword,
2294 .disp = -frame_layout.save_reg_list.size(),
2295 } },
2296 })),
2297 } },
2284 self.mir_instructions.set(backpatch_stack_dealloc, switch (-frame_layout.save_reg_list.size(self.target)) {
2285 0 => .{
2286 .tag = .mov,
2287 .ops = .rr,
2288 .data = .{ .rr = .{
2289 .r1 = .rsp,
2290 .r2 = .rbp,
2291 } },
2292 },
2293 else => |disp| .{
2294 .tag = .lea,
2295 .ops = .rm,
2296 .data = .{ .rx = .{
2297 .r1 = .rsp,
2298 .payload = try self.addExtra(Mir.Memory.encode(.{
2299 .base = .{ .reg = .rbp },
2300 .mod = .{ .rm = .{
2301 .size = .qword,
2302 .disp = disp,
2303 } },
2304 })),
2305 } },
2306 },
22982307 });
22992308 }
23002309 if (need_save_reg) {
......@@ -9552,8 +9561,7 @@ fn genLazy(self: *CodeGen, lazy_sym: link.File.LazySymbol) InnerError!void {
95529561 const enum_ty: Type = .fromInterned(lazy_sym.ty);
95539562 wip_mir_log.debug("{}.@tagName:", .{enum_ty.fmt(pt)});
95549563
9555 const resolved_cc = abi.resolveCallingConvention(.auto, self.target.*);
9556 const param_regs = abi.getCAbiIntParamRegs(resolved_cc);
9564 const param_regs = abi.getCAbiIntParamRegs(.auto);
95579565 const param_locks = self.register_manager.lockRegsAssumeUnused(2, param_regs[0..2].*);
95589566 defer for (param_locks) |lock| self.register_manager.unlockReg(lock);
95599567
......@@ -9700,7 +9708,7 @@ fn setFrameLoc(
97009708 offset.* += self.frame_allocs.items(.abi_size)[frame_i];
97019709}
97029710
9703fn computeFrameLayout(self: *CodeGen, cc: std.builtin.CallingConvention) !FrameLayout {
9711fn computeFrameLayout(self: *CodeGen, cc: std.builtin.CallingConvention.Tag) !FrameLayout {
97049712 const frame_allocs_len = self.frame_allocs.len;
97059713 try self.frame_locs.resize(self.gpa, frame_allocs_len);
97069714 const stack_frame_order = try self.gpa.alloc(FrameIndex, frame_allocs_len - FrameIndex.named_count);
......@@ -9732,8 +9740,7 @@ fn computeFrameLayout(self: *CodeGen, cc: std.builtin.CallingConvention) !FrameL
97329740 // Create list of registers to save in the prologue.
97339741 // TODO handle register classes
97349742 var save_reg_list: Mir.RegisterList = .empty;
9735 const callee_preserved_regs =
9736 abi.getCalleePreservedRegs(abi.resolveCallingConvention(cc, self.target.*));
9743 const callee_preserved_regs = abi.getCalleePreservedRegs(cc);
97379744 for (callee_preserved_regs) |reg| {
97389745 if (self.register_manager.isRegAllocated(reg)) {
97399746 save_reg_list.push(callee_preserved_regs, reg);
......@@ -9747,7 +9754,7 @@ fn computeFrameLayout(self: *CodeGen, cc: std.builtin.CallingConvention) !FrameL
97479754 const stack_frame_align_offset = if (need_align_stack)
97489755 0
97499756 else
9750 save_reg_list.size() + frame_offset[@intFromEnum(FrameIndex.args_frame)];
9757 save_reg_list.size(self.target) + frame_offset[@intFromEnum(FrameIndex.args_frame)];
97519758
97529759 var rsp_offset: i32 = 0;
97539760 self.setFrameLoc(.call_frame, .rsp, &rsp_offset, true);
......@@ -10036,10 +10043,9 @@ pub fn spillEflagsIfOccupied(self: *CodeGen) !void {
1003610043 }
1003710044}
1003810045
10039pub fn spillCallerPreservedRegs(self: *CodeGen, cc: std.builtin.CallingConvention) !void {
10046pub fn spillCallerPreservedRegs(self: *CodeGen, cc: std.builtin.CallingConvention.Tag) !void {
1004010047 switch (cc) {
10041 .x86_64_sysv => try self.spillRegisters(abi.getCallerPreservedRegs(.{ .x86_64_sysv = .{} })),
10042 .x86_64_win => try self.spillRegisters(abi.getCallerPreservedRegs(.{ .x86_64_win = .{} })),
10048 inline .auto, .x86_64_sysv, .x86_64_win => |tag| try self.spillRegisters(abi.getCallerPreservedRegs(tag)),
1004310049 else => unreachable,
1004410050 }
1004510051}
......@@ -19127,8 +19133,7 @@ fn airArg(self: *CodeGen, inst: Air.Inst.Index) !void {
1912719133 try self.spillEflagsIfOccupied();
1912819134
1912919135 const fn_info = zcu.typeToFunc(self.fn_type).?;
19130 const cc = abi.resolveCallingConvention(fn_info.cc, self.target.*);
19131 const param_int_regs = abi.getCAbiIntParamRegs(cc);
19136 const param_int_regs = abi.getCAbiIntParamRegs(fn_info.cc);
1913219137 var prev_reg: Register = undefined;
1913319138 for (
1913419139 param_int_regs[param_int_regs.len - regs_frame_addr.regs ..],
......@@ -19228,8 +19233,7 @@ fn airDbgArg(self: *CodeGen, inst: Air.Inst.Index) !void {
1922819233}
1922919234
1923019235fn airDbgVarArgs(self: *CodeGen) !void {
19231 if (self.pt.zcu.typeToFunc(self.fn_type).?.is_var_args)
19232 try self.asmPseudo(.pseudo_dbg_var_args_none);
19236 if (self.pt.zcu.typeToFunc(self.fn_type).?.is_var_args) try self.asmPseudo(.pseudo_dbg_var_args_none);
1923319237}
1923419238
1923519239fn genLocalDebugInfo(
......@@ -19364,7 +19368,6 @@ fn genCall(self: *CodeGen, info: union(enum) {
1936419368 }),
1936519369 };
1936619370 const fn_info = zcu.typeToFunc(fn_ty).?;
19367 const resolved_cc = abi.resolveCallingConvention(fn_info.cc, self.target.*);
1936819371
1936919372 const ExpectedContents = extern struct {
1937019373 var_args: [16][@sizeOf(Type)]u8 align(@alignOf(Type)),
......@@ -19406,7 +19409,7 @@ fn genCall(self: *CodeGen, info: union(enum) {
1940619409 }
1940719410
1940819411 try self.spillEflagsIfOccupied();
19409 try self.spillCallerPreservedRegs(resolved_cc);
19412 try self.spillCallerPreservedRegs(fn_info.cc);
1941019413
1941119414 // set stack arguments first because this can clobber registers
1941219415 // also clobber spill arguments as we go
......@@ -19482,7 +19485,7 @@ fn genCall(self: *CodeGen, info: union(enum) {
1948219485 );
1948319486 _ = try self.asmJccReloc(.b, loop);
1948419487
19485 const param_int_regs = abi.getCAbiIntParamRegs(resolved_cc);
19488 const param_int_regs = abi.getCAbiIntParamRegs(fn_info.cc);
1948619489 for (param_int_regs[param_int_regs.len - regs_frame_addr.regs ..]) |dst_reg| {
1948719490 try self.register_manager.getReg(dst_reg, null);
1948819491 try reg_locks.append(self.register_manager.lockReg(dst_reg));
......@@ -19546,7 +19549,7 @@ fn genCall(self: *CodeGen, info: union(enum) {
1954619549 };
1954719550 defer if (src_lock) |lock| self.register_manager.unlockReg(lock);
1954819551
19549 const param_int_regs = abi.getCAbiIntParamRegs(resolved_cc);
19552 const param_int_regs = abi.getCAbiIntParamRegs(fn_info.cc);
1955019553 for (
1955119554 param_int_regs[param_int_regs.len - regs_frame_addr.regs ..],
1955219555 0..,
......@@ -19559,8 +19562,7 @@ fn genCall(self: *CodeGen, info: union(enum) {
1955919562 else => unreachable,
1956019563 };
1956119564
19562 if (fn_info.is_var_args)
19563 try self.asmRegisterImmediate(.{ ._, .mov }, .al, .u(call_info.fp_count));
19565 if (fn_info.is_var_args) try self.asmRegisterImmediate(.{ ._, .mov }, .al, .u(call_info.fp_count));
1956419566
1956519567 // Due to incremental compilation, how function calls are generated depends
1956619568 // on linking.
......@@ -19582,8 +19584,9 @@ fn genCall(self: *CodeGen, info: union(enum) {
1958219584 } else if (self.bin_file.cast(.coff)) |coff_file| {
1958319585 const atom = try coff_file.getOrCreateAtomForNav(func.owner_nav);
1958419586 const sym_index = coff_file.getAtom(atom).getSymbolIndex().?;
19585 try self.genSetReg(.rax, .usize, .{ .lea_got = sym_index }, .{});
19586 try self.asmRegister(.{ ._, .call }, .rax);
19587 const scratch_reg = abi.getCAbiLinkerScratchReg(fn_info.cc);
19588 try self.genSetReg(scratch_reg, .usize, .{ .lea_got = sym_index }, .{});
19589 try self.asmRegister(.{ ._, .call }, scratch_reg);
1958719590 } else if (self.bin_file.cast(.macho)) |macho_file| {
1958819591 const zo = macho_file.getZigObject().?;
1958919592 const sym_index = try zo.getOrCreateMetadataForNav(macho_file, func.owner_nav);
......@@ -19622,8 +19625,9 @@ fn genCall(self: *CodeGen, info: union(enum) {
1962219625 }
1962319626 } else {
1962419627 assert(self.typeOf(callee).zigTypeTag(zcu) == .pointer);
19625 try self.genSetReg(.rax, .usize, .{ .air_ref = callee }, .{});
19626 try self.asmRegister(.{ ._, .call }, .rax);
19628 const scratch_reg = abi.getCAbiLinkerScratchReg(fn_info.cc);
19629 try self.genSetReg(scratch_reg, .usize, .{ .air_ref = callee }, .{});
19630 try self.asmRegister(.{ ._, .call }, scratch_reg);
1962719631 },
1962819632 .lib => |lib| if (self.bin_file.cast(.elf)) |elf_file| {
1962919633 const target_sym_index = try elf_file.getGlobalSymbol(lib.callee, lib.lib);
......@@ -23095,11 +23099,12 @@ fn genExternSymbolRef(
2309523099) InnerError!void {
2309623100 if (self.bin_file.cast(.coff)) |coff_file| {
2309723101 const global_index = try coff_file.getGlobalSymbol(callee, lib);
23102 const scratch_reg = abi.getCAbiLinkerScratchReg(self.fn_type.fnCallingConvention(self.pt.zcu));
2309823103 _ = try self.addInst(.{
2309923104 .tag = .mov,
2310023105 .ops = .import_reloc,
2310123106 .data = .{ .rx = .{
23102 .r1 = .rax,
23107 .r1 = scratch_reg,
2310323108 .payload = try self.addExtra(bits.SymbolOffset{
2310423109 .sym_index = link.File.Coff.global_symbol_bit | global_index,
2310523110 }),
......@@ -23107,7 +23112,7 @@ fn genExternSymbolRef(
2310723112 });
2310823113 switch (tag) {
2310923114 .mov => {},
23110 .call => try self.asmRegister(.{ ._, .call }, .rax),
23115 .call => try self.asmRegister(.{ ._, .call }, scratch_reg),
2311123116 else => unreachable,
2311223117 }
2311323118 } else return self.fail("TODO implement calling extern functions", .{});
......@@ -24226,7 +24231,6 @@ fn airTagName(self: *CodeGen, inst: Air.Inst.Index) !void {
2422624231 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
2422724232 const inst_ty = self.typeOfIndex(inst);
2422824233 const enum_ty = self.typeOf(un_op);
24229 const resolved_cc = abi.resolveCallingConvention(.auto, self.target.*);
2423024234
2423124235 // We need a properly aligned and sized call frame to be able to call this function.
2423224236 {
......@@ -24244,9 +24248,9 @@ fn airTagName(self: *CodeGen, inst: Air.Inst.Index) !void {
2424424248 }
2424524249
2424624250 try self.spillEflagsIfOccupied();
24247 try self.spillCallerPreservedRegs(resolved_cc);
24251 try self.spillCallerPreservedRegs(.auto);
2424824252
24249 const param_regs = abi.getCAbiIntParamRegs(resolved_cc);
24253 const param_regs = abi.getCAbiIntParamRegs(.auto);
2425024254
2425124255 const dst_mcv = try self.allocRegOrMem(inst, false);
2425224256 try self.genSetReg(param_regs[0], .usize, dst_mcv.address(), .{});
......@@ -24255,7 +24259,7 @@ fn airTagName(self: *CodeGen, inst: Air.Inst.Index) !void {
2425524259 try self.genSetReg(param_regs[1], enum_ty, operand, .{});
2425624260
2425724261 const enum_lazy_sym: link.File.LazySymbol = .{ .kind = .code, .ty = enum_ty.toIntern() };
24258 try self.genLazySymbolRef(.call, .rax, enum_lazy_sym);
24262 try self.genLazySymbolRef(.call, abi.getCAbiLinkerScratchReg(self.fn_type.fnCallingConvention(zcu)), enum_lazy_sym);
2425924263
2426024264 return self.finishAir(inst, dst_mcv, .{ un_op, .none, .none });
2426124265}
......@@ -26349,10 +26353,7 @@ fn airVaStart(self: *CodeGen, inst: Air.Inst.Index) !void {
2634926353 const va_list_ty = self.air.instructions.items(.data)[@intFromEnum(inst)].ty;
2635026354 const ptr_anyopaque_ty = try pt.singleMutPtrType(.anyopaque);
2635126355
26352 const result: MCValue = switch (abi.resolveCallingConvention(
26353 self.fn_type.fnCallingConvention(zcu),
26354 self.target.*,
26355 )) {
26356 const result: MCValue = switch (self.fn_type.fnCallingConvention(zcu)) {
2635626357 .x86_64_sysv => result: {
2635726358 const info = self.va_info.sysv;
2635826359 const dst_fi = try self.allocFrameIndex(.initSpill(va_list_ty, zcu));
......@@ -26396,7 +26397,7 @@ fn airVaStart(self: *CodeGen, inst: Air.Inst.Index) !void {
2639626397 break :result .{ .load_frame = .{ .index = dst_fi } };
2639726398 },
2639826399 .x86_64_win => return self.fail("TODO implement c_va_start for Win64", .{}),
26399 else => unreachable,
26400 else => |cc| return self.fail("{s} does not support var args", .{@tagName(cc)}),
2640026401 };
2640126402 return self.finishAir(inst, result, .{ .none, .none, .none });
2640226403}
......@@ -26410,10 +26411,7 @@ fn airVaArg(self: *CodeGen, inst: Air.Inst.Index) !void {
2641026411 const ptr_anyopaque_ty = try pt.singleMutPtrType(.anyopaque);
2641126412 const unused = self.liveness.isUnused(inst);
2641226413
26413 const result: MCValue = switch (abi.resolveCallingConvention(
26414 self.fn_type.fnCallingConvention(zcu),
26415 self.target.*,
26416 )) {
26414 const result: MCValue = switch (self.fn_type.fnCallingConvention(zcu)) {
2641726415 .x86_64_sysv => result: {
2641826416 try self.spillEflagsIfOccupied();
2641926417
......@@ -26592,7 +26590,7 @@ fn airVaArg(self: *CodeGen, inst: Air.Inst.Index) !void {
2659226590 break :result promote_mcv;
2659326591 },
2659426592 .x86_64_win => return self.fail("TODO implement c_va_arg for Win64", .{}),
26595 else => unreachable,
26593 else => |cc| return self.fail("{s} does not support var args", .{@tagName(cc)}),
2659626594 };
2659726595 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2659826596}
......@@ -26753,13 +26751,15 @@ fn resolveCallingConventionValues(
2675326751 errdefer self.gpa.free(result.args);
2675426752
2675526753 const ret_ty: Type = .fromInterned(fn_info.return_type);
26756
26757 const resolved_cc = abi.resolveCallingConvention(cc, self.target.*);
2675826754 switch (cc) {
2675926755 .naked => {
2676026756 assert(result.args.len == 0);
2676126757 result.return_value = .init(.unreach);
26762 result.stack_align = .@"8";
26758 result.stack_align = switch (self.target.cpu.arch) {
26759 else => unreachable,
26760 .x86 => .@"4",
26761 .x86_64 => .@"8",
26762 };
2676326763 },
2676426764 .x86_64_sysv, .x86_64_win => |cc_opts| {
2676526765 var ret_int_reg_i: u32 = 0;
......@@ -26768,17 +26768,14 @@ fn resolveCallingConventionValues(
2676826768 var param_sse_reg_i: u32 = 0;
2676926769 result.stack_align = .fromByteUnits(cc_opts.incoming_stack_alignment orelse 16);
2677026770
26771 switch (resolved_cc) {
26771 switch (cc) {
2677226772 .x86_64_sysv => {},
26773 .x86_64_win => {
26774 // Align the stack to 16bytes before allocating shadow stack space (if any).
26775 result.stack_byte_count += @intCast(4 * Type.usize.abiSize(zcu));
26776 },
26773 .x86_64_win => result.stack_byte_count += @intCast(4 * 8),
2677726774 else => unreachable,
2677826775 }
2677926776
2678026777 // Return values
26781 if (ret_ty.zigTypeTag(zcu) == .noreturn) {
26778 if (ret_ty.isNoReturn(zcu)) {
2678226779 result.return_value = .init(.unreach);
2678326780 } else if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
2678426781 // TODO: is this even possible for C calling convention?
......@@ -26787,7 +26784,7 @@ fn resolveCallingConventionValues(
2678726784 var ret_tracking: [4]InstTracking = undefined;
2678826785 var ret_tracking_i: usize = 0;
2678926786
26790 const classes = switch (resolved_cc) {
26787 const classes = switch (cc) {
2679126788 .x86_64_sysv => std.mem.sliceTo(&abi.classifySystemV(ret_ty, zcu, self.target.*, .ret), .none),
2679226789 .x86_64_win => &.{abi.classifyWindows(ret_ty, zcu)},
2679326790 else => unreachable,
......@@ -26795,7 +26792,7 @@ fn resolveCallingConventionValues(
2679526792 for (classes) |class| switch (class) {
2679626793 .integer => {
2679726794 const ret_int_reg = registerAlias(
26798 abi.getCAbiIntReturnRegs(resolved_cc)[ret_int_reg_i],
26795 abi.getCAbiIntReturnRegs(cc)[ret_int_reg_i],
2679926796 @intCast(@min(ret_ty.abiSize(zcu), 8)),
2680026797 );
2680126798 ret_int_reg_i += 1;
......@@ -26804,7 +26801,7 @@ fn resolveCallingConventionValues(
2680426801 ret_tracking_i += 1;
2680526802 },
2680626803 .sse, .float, .float_combine, .win_i128 => {
26807 const ret_sse_regs = abi.getCAbiSseReturnRegs(resolved_cc);
26804 const ret_sse_regs = abi.getCAbiSseReturnRegs(cc);
2680826805 const abi_size: u32 = @intCast(ret_ty.abiSize(zcu));
2680926806 const reg_size = @min(abi_size, self.vectorSize(.float));
2681026807 var byte_offset: u32 = 0;
......@@ -26818,18 +26815,18 @@ fn resolveCallingConventionValues(
2681826815 },
2681926816 .sseup => assert(ret_tracking[ret_tracking_i - 1].short.register.class() == .sse),
2682026817 .x87 => {
26821 ret_tracking[ret_tracking_i] = .init(.{ .register = .st0 });
26818 ret_tracking[ret_tracking_i] = .init(.{ .register = abi.getCAbiX87ReturnRegs(cc)[0] });
2682226819 ret_tracking_i += 1;
2682326820 },
2682426821 .x87up => assert(ret_tracking[ret_tracking_i - 1].short.register.class() == .x87),
2682526822 .complex_x87 => {
26826 ret_tracking[ret_tracking_i] = .init(.{ .register_pair = .{ .st0, .st1 } });
26823 ret_tracking[ret_tracking_i] = .init(.{ .register_pair = abi.getCAbiX87ReturnRegs(cc)[0..2].* });
2682726824 ret_tracking_i += 1;
2682826825 },
2682926826 .memory => {
26830 const ret_int_reg = abi.getCAbiIntReturnRegs(resolved_cc)[ret_int_reg_i].to64();
26827 const ret_int_reg = abi.getCAbiIntReturnRegs(cc)[ret_int_reg_i].to64();
2683126828 ret_int_reg_i += 1;
26832 const ret_indirect_reg = abi.getCAbiIntParamRegs(resolved_cc)[param_int_reg_i];
26829 const ret_indirect_reg = abi.getCAbiIntParamRegs(cc)[param_int_reg_i];
2683326830 param_int_reg_i += 1;
2683426831
2683526832 ret_tracking[ret_tracking_i] = .{
......@@ -26864,7 +26861,7 @@ fn resolveCallingConventionValues(
2686426861 // Input params
2686526862 for (param_types, result.args) |ty, *arg| {
2686626863 assert(ty.hasRuntimeBitsIgnoreComptime(zcu));
26867 switch (resolved_cc) {
26864 switch (cc) {
2686826865 .x86_64_sysv => {},
2686926866 .x86_64_win => {
2687026867 param_int_reg_i = @max(param_int_reg_i, param_sse_reg_i);
......@@ -26876,14 +26873,14 @@ fn resolveCallingConventionValues(
2687626873 var arg_mcv: [4]MCValue = undefined;
2687726874 var arg_mcv_i: usize = 0;
2687826875
26879 const classes = switch (resolved_cc) {
26876 const classes = switch (cc) {
2688026877 .x86_64_sysv => std.mem.sliceTo(&abi.classifySystemV(ty, zcu, self.target.*, .arg), .none),
2688126878 .x86_64_win => &.{abi.classifyWindows(ty, zcu)},
2688226879 else => unreachable,
2688326880 };
2688426881 classes: for (classes) |class| switch (class) {
2688526882 .integer => {
26886 const param_int_regs = abi.getCAbiIntParamRegs(resolved_cc);
26883 const param_int_regs = abi.getCAbiIntParamRegs(cc);
2688726884 if (param_int_reg_i >= param_int_regs.len) break;
2688826885
2688926886 const param_int_reg =
......@@ -26894,7 +26891,7 @@ fn resolveCallingConventionValues(
2689426891 arg_mcv_i += 1;
2689526892 },
2689626893 .sse, .float, .float_combine => {
26897 const param_sse_regs = abi.getCAbiSseParamRegs(resolved_cc);
26894 const param_sse_regs = abi.getCAbiSseParamRegs(cc);
2689826895 const abi_size: u32 = @intCast(ty.abiSize(zcu));
2689926896 const reg_size = @min(abi_size, self.vectorSize(.float));
2690026897 var byte_offset: u32 = 0;
......@@ -26909,14 +26906,13 @@ fn resolveCallingConventionValues(
2690926906 }
2691026907 },
2691126908 .sseup => assert(arg_mcv[arg_mcv_i - 1].register.class() == .sse),
26912 .x87, .x87up, .complex_x87, .memory, .win_i128 => switch (resolved_cc) {
26909 .x87, .x87up, .complex_x87, .memory, .win_i128 => switch (cc) {
2691326910 .x86_64_sysv => switch (class) {
2691426911 .x87, .x87up, .complex_x87, .memory => break,
2691526912 else => unreachable,
2691626913 },
2691726914 .x86_64_win => if (ty.abiSize(zcu) > 8) {
26918 const param_int_reg =
26919 abi.getCAbiIntParamRegs(resolved_cc)[param_int_reg_i].to64();
26915 const param_int_reg = abi.getCAbiIntParamRegs(cc)[param_int_reg_i].to64();
2692026916 param_int_reg_i += 1;
2692126917
2692226918 arg_mcv[arg_mcv_i] = .{ .indirect = .{ .reg = param_int_reg } };
......@@ -26927,7 +26923,7 @@ fn resolveCallingConventionValues(
2692726923 .none => unreachable,
2692826924 .integer_per_element => {
2692926925 const param_int_regs_len: u32 =
26930 @intCast(abi.getCAbiIntParamRegs(resolved_cc).len);
26926 @intCast(abi.getCAbiIntParamRegs(cc).len);
2693126927 const remaining_param_int_regs: u3 =
2693226928 @intCast(param_int_regs_len - param_int_reg_i);
2693326929 param_int_reg_i = param_int_regs_len;
......@@ -26989,42 +26985,86 @@ fn resolveCallingConventionValues(
2698926985 result.fp_count = param_sse_reg_i;
2699026986 },
2699126987 .auto => {
26992 result.stack_align = .@"16";
26988 result.stack_align = abi.zigcc.stack_align orelse .fromByteUnits(self.vectorSize(.float));
26989
26990 var param_gpr = abi.getCAbiIntParamRegs(cc);
26991 var param_x87 = abi.getCAbiX87ParamRegs(cc);
26992 var param_sse = abi.getCAbiSseParamRegs(cc);
2699326993
2699426994 // Return values
26995 if (ret_ty.zigTypeTag(zcu) == .noreturn) {
26996 result.return_value = .init(.unreach);
26997 } else if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
26998 result.return_value = .init(.none);
26999 } else {
27000 const ret_reg = abi.getCAbiIntReturnRegs(resolved_cc)[0];
27001 const ret_ty_size: u31 = @intCast(ret_ty.abiSize(zcu));
27002 if (ret_ty_size <= 8 and !ret_ty.isRuntimeFloat()) {
27003 const aliased_reg = registerAlias(ret_reg, ret_ty_size);
27004 result.return_value = .{ .short = .{ .register = aliased_reg }, .long = .none };
27005 } else {
27006 const ret_indirect_reg = abi.getCAbiIntParamRegs(resolved_cc)[0];
27007 result.return_value = .{
27008 .short = .{ .indirect = .{ .reg = ret_reg } },
27009 .long = .{ .indirect = .{ .reg = ret_indirect_reg } },
27010 };
27011 }
27012 }
26995 result.return_value = if (ret_ty.isNoReturn(zcu))
26996 .init(.unreach)
26997 else if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu))
26998 .init(.none)
26999 else return_value: {
27000 const ret_gpr = abi.getCAbiIntReturnRegs(cc);
27001 const ret_size: u31 = @intCast(ret_ty.abiSize(zcu));
27002 if (abi.zigcc.return_in_regs) switch (self.regClassForType(ret_ty)) {
27003 .general_purpose => if (ret_size <= @as(u4, switch (self.target.cpu.arch) {
27004 else => unreachable,
27005 .x86 => 4,
27006 .x86_64 => 8,
27007 }))
27008 break :return_value .init(.{ .register = registerAlias(ret_gpr[0], ret_size) })
27009 else if (ret_gpr.len >= 2 and ret_ty.isSliceAtRuntime(zcu))
27010 break :return_value .init(.{ .register_pair = ret_gpr[0..2].* }),
27011 .segment, .mmx, .ip => unreachable,
27012 .x87 => break :return_value .init(.{ .register = .st0 }),
27013 .sse => if (ret_size <= self.vectorSize(.float)) break :return_value .init(.{
27014 .register = registerAlias(abi.getCAbiSseReturnRegs(cc)[0], @max(ret_size, 16)),
27015 }),
27016 };
27017 const ret_indirect_reg = param_gpr[0];
27018 param_gpr = param_gpr[1..];
27019 break :return_value .{
27020 .short = .{ .indirect = .{ .reg = ret_gpr[0] } },
27021 .long = .{ .indirect = .{ .reg = ret_indirect_reg } },
27022 };
27023 };
2701327024
2701427025 // Input params
27015 for (param_types, result.args) |ty, *arg| {
27016 if (!ty.hasRuntimeBitsIgnoreComptime(zcu)) {
27026 for (param_types, result.args) |param_ty, *arg| {
27027 if (!param_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
2701727028 arg.* = .none;
2701827029 continue;
2701927030 }
27020 const param_align = ty.abiAlignment(zcu);
27031 const param_size: u31 = @intCast(param_ty.abiSize(zcu));
27032 if (abi.zigcc.params_in_regs) switch (self.regClassForType(param_ty)) {
27033 .general_purpose => if (param_gpr.len >= 1 and param_size <= @as(u4, switch (self.target.cpu.arch) {
27034 else => unreachable,
27035 .x86 => 4,
27036 .x86_64 => 8,
27037 })) {
27038 arg.* = .{ .register = registerAlias(param_gpr[0], param_size) };
27039 param_gpr = param_gpr[1..];
27040 continue;
27041 } else if (param_gpr.len >= 2 and param_ty.isSliceAtRuntime(zcu)) {
27042 arg.* = .{ .register_pair = param_gpr[0..2].* };
27043 param_gpr = param_gpr[2..];
27044 continue;
27045 },
27046 .segment, .mmx, .ip => unreachable,
27047 .x87 => if (param_x87.len >= 1) {
27048 arg.* = .{ .register = param_x87[0] };
27049 param_x87 = param_x87[1..];
27050 continue;
27051 },
27052 .sse => if (param_sse.len >= 1 and param_size <= self.vectorSize(.float)) {
27053 arg.* = .{
27054 .register = registerAlias(param_sse[0], @max(param_size, 16)),
27055 };
27056 param_sse = param_sse[1..];
27057 continue;
27058 },
27059 };
27060 const param_align = param_ty.abiAlignment(zcu);
2702127061 result.stack_byte_count = @intCast(param_align.forward(result.stack_byte_count));
2702227062 result.stack_align = result.stack_align.max(param_align);
2702327063 arg.* = .{ .load_frame = .{
2702427064 .index = stack_frame_base,
2702527065 .off = result.stack_byte_count,
2702627066 } };
27027 result.stack_byte_count += @intCast(ty.abiSize(zcu));
27067 result.stack_byte_count += param_size;
2702827068 }
2702927069 },
2703027070 else => return self.fail("TODO implement function parameters and return values for {} on x86_64", .{cc}),
src/arch/x86_64/Mir.zig+6-2
......@@ -1200,8 +1200,12 @@ pub const RegisterList = struct {
12001200 return @intCast(self.bitset.count());
12011201 }
12021202
1203 pub fn size(self: Self) i32 {
1204 return @intCast(self.bitset.count() * 8);
1203 pub fn size(self: Self, target: *const std.Target) i32 {
1204 return @intCast(self.bitset.count() * @as(u4, switch (target.cpu.arch) {
1205 else => unreachable,
1206 .x86 => 4,
1207 .x86_64 => 8,
1208 }));
12051209 }
12061210};
12071211
src/arch/x86_64/abi.zig+64-19
......@@ -408,6 +408,31 @@ fn classifySystemVUnion(
408408 return starting_byte_offset + loaded_union.sizeUnordered(ip);
409409}
410410
411pub const zigcc = struct {
412 pub const stack_align: ?InternPool.Alignment = null;
413 pub const return_in_regs = true;
414 pub const params_in_regs = true;
415
416 const volatile_gpr = gp_regs.len - 5;
417 const volatile_x87 = x87_regs.len - 1;
418 const volatile_sse = sse_avx_regs.len;
419
420 /// Note that .rsp and .rbp also belong to this set, however, we never expect to use them
421 /// for anything else but stack offset tracking therefore we exclude them from this set.
422 pub const callee_preserved_regs = gp_regs[volatile_gpr..] ++ x87_regs[volatile_x87 .. x87_regs.len - 1] ++ sse_avx_regs[volatile_sse..];
423 /// These registers need to be preserved (saved on the stack) and restored by the caller before
424 /// the caller relinquishes control to a subroutine via call instruction (or similar).
425 /// In other words, these registers are free to use by the callee.
426 pub const caller_preserved_regs = gp_regs[0..volatile_gpr] ++ x87_regs[0..volatile_x87] ++ sse_avx_regs[0..volatile_sse];
427
428 const int_param_regs = gp_regs[0 .. volatile_gpr - 1];
429 const x87_param_regs = x87_regs[0..volatile_x87];
430 const sse_param_regs = sse_avx_regs[0..volatile_sse];
431 const int_return_regs = gp_regs[0..volatile_gpr];
432 const x87_return_regs = x87_regs[0..volatile_x87];
433 const sse_return_regs = sse_avx_regs[0..volatile_gpr];
434};
435
411436pub const SysV = struct {
412437 /// Note that .rsp and .rbp also belong to this set, however, we never expect to use them
413438 /// for anything else but stack offset tracking therefore we exclude them from this set.
......@@ -418,8 +443,10 @@ pub const SysV = struct {
418443 pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 } ++ x87_regs ++ sse_avx_regs;
419444
420445 pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
446 pub const c_abi_x87_param_regs = x87_regs[0..0].*;
421447 pub const c_abi_sse_param_regs = sse_avx_regs[0..8].*;
422448 pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx };
449 pub const c_abi_x87_return_regs = x87_regs[0..2].*;
423450 pub const c_abi_sse_return_regs = sse_avx_regs[0..4].*;
424451};
425452
......@@ -433,72 +460,90 @@ pub const Win64 = struct {
433460 pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .r8, .r9, .r10, .r11 } ++ x87_regs ++ sse_avx_regs;
434461
435462 pub const c_abi_int_param_regs = [_]Register{ .rcx, .rdx, .r8, .r9 };
463 pub const c_abi_x87_param_regs = x87_regs[0..0].*;
436464 pub const c_abi_sse_param_regs = sse_avx_regs[0..4].*;
437465 pub const c_abi_int_return_regs = [_]Register{.rax};
466 pub const c_abi_x87_return_regs = x87_regs[0..0].*;
438467 pub const c_abi_sse_return_regs = sse_avx_regs[0..1].*;
439468};
440469
441pub fn resolveCallingConvention(
442 cc: std.builtin.CallingConvention,
443 target: std.Target,
444) std.builtin.CallingConvention {
445 return switch (cc) {
446 .auto => switch (target.os.tag) {
447 else => .{ .x86_64_sysv = .{} },
448 .windows => .{ .x86_64_win = .{} },
449 },
450 else => cc,
451 };
452}
453
454pub fn getCalleePreservedRegs(cc: std.builtin.CallingConvention) []const Register {
470pub fn getCalleePreservedRegs(cc: std.builtin.CallingConvention.Tag) []const Register {
455471 return switch (cc) {
472 .auto => zigcc.callee_preserved_regs,
456473 .x86_64_sysv => &SysV.callee_preserved_regs,
457474 .x86_64_win => &Win64.callee_preserved_regs,
458475 else => unreachable,
459476 };
460477}
461478
462pub fn getCallerPreservedRegs(cc: std.builtin.CallingConvention) []const Register {
479pub fn getCallerPreservedRegs(cc: std.builtin.CallingConvention.Tag) []const Register {
463480 return switch (cc) {
481 .auto => zigcc.caller_preserved_regs,
464482 .x86_64_sysv => &SysV.caller_preserved_regs,
465483 .x86_64_win => &Win64.caller_preserved_regs,
466484 else => unreachable,
467485 };
468486}
469487
470pub fn getCAbiIntParamRegs(cc: std.builtin.CallingConvention) []const Register {
488pub fn getCAbiIntParamRegs(cc: std.builtin.CallingConvention.Tag) []const Register {
471489 return switch (cc) {
490 .auto => zigcc.int_param_regs,
472491 .x86_64_sysv => &SysV.c_abi_int_param_regs,
473492 .x86_64_win => &Win64.c_abi_int_param_regs,
474493 else => unreachable,
475494 };
476495}
477496
478pub fn getCAbiSseParamRegs(cc: std.builtin.CallingConvention) []const Register {
497pub fn getCAbiX87ParamRegs(cc: std.builtin.CallingConvention.Tag) []const Register {
479498 return switch (cc) {
499 .auto => zigcc.x87_param_regs,
500 .x86_64_sysv => &SysV.c_abi_x87_param_regs,
501 .x86_64_win => &Win64.c_abi_x87_param_regs,
502 else => unreachable,
503 };
504}
505
506pub fn getCAbiSseParamRegs(cc: std.builtin.CallingConvention.Tag) []const Register {
507 return switch (cc) {
508 .auto => zigcc.sse_param_regs,
480509 .x86_64_sysv => &SysV.c_abi_sse_param_regs,
481510 .x86_64_win => &Win64.c_abi_sse_param_regs,
482511 else => unreachable,
483512 };
484513}
485514
486pub fn getCAbiIntReturnRegs(cc: std.builtin.CallingConvention) []const Register {
515pub fn getCAbiIntReturnRegs(cc: std.builtin.CallingConvention.Tag) []const Register {
487516 return switch (cc) {
517 .auto => zigcc.int_return_regs,
488518 .x86_64_sysv => &SysV.c_abi_int_return_regs,
489519 .x86_64_win => &Win64.c_abi_int_return_regs,
490520 else => unreachable,
491521 };
492522}
493523
494pub fn getCAbiSseReturnRegs(cc: std.builtin.CallingConvention) []const Register {
524pub fn getCAbiX87ReturnRegs(cc: std.builtin.CallingConvention.Tag) []const Register {
495525 return switch (cc) {
526 .auto => zigcc.x87_return_regs,
527 .x86_64_sysv => &SysV.c_abi_x87_return_regs,
528 .x86_64_win => &Win64.c_abi_x87_return_regs,
529 else => unreachable,
530 };
531}
532
533pub fn getCAbiSseReturnRegs(cc: std.builtin.CallingConvention.Tag) []const Register {
534 return switch (cc) {
535 .auto => zigcc.sse_return_regs,
496536 .x86_64_sysv => &SysV.c_abi_sse_return_regs,
497537 .x86_64_win => &Win64.c_abi_sse_return_regs,
498538 else => unreachable,
499539 };
500540}
501541
542pub fn getCAbiLinkerScratchReg(cc: std.builtin.CallingConvention.Tag) Register {
543 const int_return_regs = getCAbiIntReturnRegs(cc);
544 return int_return_regs[int_return_regs.len - 1];
545}
546
502547const gp_regs = [_]Register{
503548 .rax, .rdx, .rbx, .rcx, .rsi, .rdi, .r8, .r9, .r10, .r11, .r12, .r13, .r14, .r15,
504549};