authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-10-04 19:24:31-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-10-05 00:19:25-04:00
logc2ec518fe2704422017b09c0808ec1a40c0ec0ed
tree6c9603b9b2a4b53783906b6bed81a12eb09725e1
parentb4427bc300cd768721891e6747e59102c8cb06fc

x86_64: refactor calling convention checks


3 files changed, 69 insertions(+), 47 deletions(-)

src/arch/x86_64/CodeGen.zig+33-25
...@@ -807,7 +807,7 @@ pub fn generate(...@@ -807,7 +807,7 @@ pub fn generate(
807 .lower = .{807 .lower = .{
808 .allocator = bin_file.allocator,808 .allocator = bin_file.allocator,
809 .mir = mir,809 .mir = mir,
810 .target = &bin_file.options.target,810 .cc = abi.resolveCallingConvention(fn_info.cc, function.target.*),
811 .src_loc = src_loc,811 .src_loc = src_loc,
812 },812 },
813 .bin_file = bin_file,813 .bin_file = bin_file,
...@@ -893,7 +893,7 @@ pub fn generateLazy(...@@ -893,7 +893,7 @@ pub fn generateLazy(
893 .lower = .{893 .lower = .{
894 .allocator = bin_file.allocator,894 .allocator = bin_file.allocator,
895 .mir = mir,895 .mir = mir,
896 .target = &bin_file.options.target,896 .cc = abi.resolveCallingConvention(.Unspecified, function.target.*),
897 .src_loc = src_loc,897 .src_loc = src_loc,
898 },898 },
899 .bin_file = bin_file,899 .bin_file = bin_file,
...@@ -980,6 +980,7 @@ fn formatWipMir(...@@ -980,6 +980,7 @@ fn formatWipMir(
980 _: std.fmt.FormatOptions,980 _: std.fmt.FormatOptions,
981 writer: anytype,981 writer: anytype,
982) @TypeOf(writer).Error!void {982) @TypeOf(writer).Error!void {
983 const mod = data.self.bin_file.options.module.?;
983 var lower = Lower{984 var lower = Lower{
984 .allocator = data.self.gpa,985 .allocator = data.self.gpa,
985 .mir = .{986 .mir = .{
...@@ -987,7 +988,7 @@ fn formatWipMir(...@@ -987,7 +988,7 @@ fn formatWipMir(
987 .extra = data.self.mir_extra.items,988 .extra = data.self.mir_extra.items,
988 .frame_locs = (std.MultiArrayList(Mir.FrameLoc){}).slice(),989 .frame_locs = (std.MultiArrayList(Mir.FrameLoc){}).slice(),
989 },990 },
990 .target = data.self.target,991 .cc = mod.typeToFunc(data.self.fn_type).?.cc,
991 .src_loc = data.self.src_loc,992 .src_loc = data.self.src_loc,
992 };993 };
993 for ((lower.lowerMir(data.inst) catch |err| switch (err) {994 for ((lower.lowerMir(data.inst) catch |err| switch (err) {
...@@ -1680,7 +1681,7 @@ fn gen(self: *Self) InnerError!void {...@@ -1680,7 +1681,7 @@ fn gen(self: *Self) InnerError!void {
1680 try self.asmRegister(.{ ._, .pop }, .rbp);1681 try self.asmRegister(.{ ._, .pop }, .rbp);
1681 try self.asmOpOnly(.{ ._, .ret });1682 try self.asmOpOnly(.{ ._, .ret });
16821683
1683 const frame_layout = try self.computeFrameLayout();1684 const frame_layout = try self.computeFrameLayout(cc);
1684 const need_frame_align = frame_layout.stack_mask != math.maxInt(u32);1685 const need_frame_align = frame_layout.stack_mask != math.maxInt(u32);
1685 const need_stack_adjust = frame_layout.stack_adjust > 0;1686 const need_stack_adjust = frame_layout.stack_adjust > 0;
1686 const need_save_reg = frame_layout.save_reg_list.count() > 0;1687 const need_save_reg = frame_layout.save_reg_list.count() > 0;
...@@ -2075,7 +2076,8 @@ fn genLazy(self: *Self, lazy_sym: link.File.LazySymbol) InnerError!void {...@@ -2075,7 +2076,8 @@ fn genLazy(self: *Self, lazy_sym: link.File.LazySymbol) InnerError!void {
2075 const enum_ty = lazy_sym.ty;2076 const enum_ty = lazy_sym.ty;
2076 wip_mir_log.debug("{}.@tagName:", .{enum_ty.fmt(self.bin_file.options.module.?)});2077 wip_mir_log.debug("{}.@tagName:", .{enum_ty.fmt(self.bin_file.options.module.?)});
20772078
2078 const param_regs = abi.getCAbiIntParamRegs(self.target.*);2079 const resolved_cc = abi.resolveCallingConvention(.Unspecified, self.target.*);
2080 const param_regs = abi.getCAbiIntParamRegs(resolved_cc);
2079 const param_locks = self.register_manager.lockRegsAssumeUnused(2, param_regs[0..2].*);2081 const param_locks = self.register_manager.lockRegsAssumeUnused(2, param_regs[0..2].*);
2080 defer for (param_locks) |lock| self.register_manager.unlockReg(lock);2082 defer for (param_locks) |lock| self.register_manager.unlockReg(lock);
20812083
...@@ -2208,7 +2210,7 @@ fn setFrameLoc(...@@ -2208,7 +2210,7 @@ fn setFrameLoc(
2208 offset.* += self.frame_allocs.items(.abi_size)[frame_i];2210 offset.* += self.frame_allocs.items(.abi_size)[frame_i];
2209}2211}
22102212
2211fn computeFrameLayout(self: *Self) !FrameLayout {2213fn computeFrameLayout(self: *Self, cc: std.builtin.CallingConvention) !FrameLayout {
2212 const frame_allocs_len = self.frame_allocs.len;2214 const frame_allocs_len = self.frame_allocs.len;
2213 try self.frame_locs.resize(self.gpa, frame_allocs_len);2215 try self.frame_locs.resize(self.gpa, frame_allocs_len);
2214 const stack_frame_order = try self.gpa.alloc(FrameIndex, frame_allocs_len - FrameIndex.named_count);2216 const stack_frame_order = try self.gpa.alloc(FrameIndex, frame_allocs_len - FrameIndex.named_count);
...@@ -2240,7 +2242,8 @@ fn computeFrameLayout(self: *Self) !FrameLayout {...@@ -2240,7 +2242,8 @@ fn computeFrameLayout(self: *Self) !FrameLayout {
2240 // Create list of registers to save in the prologue.2242 // Create list of registers to save in the prologue.
2241 // TODO handle register classes2243 // TODO handle register classes
2242 var save_reg_list = Mir.RegisterList{};2244 var save_reg_list = Mir.RegisterList{};
2243 const callee_preserved_regs = abi.getCalleePreservedRegs(self.target.*);2245 const callee_preserved_regs =
2246 abi.getCalleePreservedRegs(abi.resolveCallingConvention(cc, self.target.*));
2244 for (callee_preserved_regs) |reg| {2247 for (callee_preserved_regs) |reg| {
2245 if (self.register_manager.isRegAllocated(reg)) {2248 if (self.register_manager.isRegAllocated(reg)) {
2246 save_reg_list.push(callee_preserved_regs, reg);2249 save_reg_list.push(callee_preserved_regs, reg);
...@@ -8477,8 +8480,7 @@ fn genCall(self: *Self, info: union(enum) {...@@ -8477,8 +8480,7 @@ fn genCall(self: *Self, info: union(enum) {
8477 try arg_locks.ensureTotalCapacity(16);8480 try arg_locks.ensureTotalCapacity(16);
8478 defer for (arg_locks.items) |arg_lock| if (arg_lock) |lock| self.register_manager.unlockReg(lock);8481 defer for (arg_locks.items) |arg_lock| if (arg_lock) |lock| self.register_manager.unlockReg(lock);
84798482
8480 var call_info =8483 var call_info = try self.resolveCallingConventionValues(fn_info, var_args, .call_frame);
8481 try self.resolveCallingConventionValues(fn_info, var_args, .call_frame);
8482 defer call_info.deinit(self);8484 defer call_info.deinit(self);
84838485
8484 // We need a properly aligned and sized call frame to be able to call this function.8486 // We need a properly aligned and sized call frame to be able to call this function.
...@@ -8497,7 +8499,9 @@ fn genCall(self: *Self, info: union(enum) {...@@ -8497,7 +8499,9 @@ fn genCall(self: *Self, info: union(enum) {
8497 }8499 }
84988500
8499 try self.spillEflagsIfOccupied();8501 try self.spillEflagsIfOccupied();
8500 try self.spillRegisters(abi.getCallerPreservedRegs(self.target.*));8502 try self.spillRegisters(abi.getCallerPreservedRegs(
8503 abi.resolveCallingConvention(fn_info.cc, self.target.*),
8504 ));
85018505
8502 // set stack arguments first because this can clobber registers8506 // set stack arguments first because this can clobber registers
8503 // also clobber spill arguments as we go8507 // also clobber spill arguments as we go
...@@ -11727,6 +11731,7 @@ fn airTagName(self: *Self, inst: Air.Inst.Index) !void {...@@ -11727,6 +11731,7 @@ fn airTagName(self: *Self, inst: Air.Inst.Index) !void {
11727 const un_op = self.air.instructions.items(.data)[inst].un_op;11731 const un_op = self.air.instructions.items(.data)[inst].un_op;
11728 const inst_ty = self.typeOfIndex(inst);11732 const inst_ty = self.typeOfIndex(inst);
11729 const enum_ty = self.typeOf(un_op);11733 const enum_ty = self.typeOf(un_op);
11734 const resolved_cc = abi.resolveCallingConvention(.Unspecified, self.target.*);
1173011735
11731 // We need a properly aligned and sized call frame to be able to call this function.11736 // We need a properly aligned and sized call frame to be able to call this function.
11732 {11737 {
...@@ -11744,9 +11749,9 @@ fn airTagName(self: *Self, inst: Air.Inst.Index) !void {...@@ -11744,9 +11749,9 @@ fn airTagName(self: *Self, inst: Air.Inst.Index) !void {
11744 }11749 }
1174511750
11746 try self.spillEflagsIfOccupied();11751 try self.spillEflagsIfOccupied();
11747 try self.spillRegisters(abi.getCallerPreservedRegs(self.target.*));11752 try self.spillRegisters(abi.getCallerPreservedRegs(resolved_cc));
1174811753
11749 const param_regs = abi.getCAbiIntParamRegs(self.target.*);11754 const param_regs = abi.getCAbiIntParamRegs(resolved_cc);
1175011755
11751 const dst_mcv = try self.allocRegOrMem(inst, false);11756 const dst_mcv = try self.allocRegOrMem(inst, false);
11752 try self.genSetReg(param_regs[0], Type.usize, dst_mcv.address());11757 try self.genSetReg(param_regs[0], Type.usize, dst_mcv.address());
...@@ -12523,23 +12528,25 @@ fn resolveCallingConventionValues(...@@ -12523,23 +12528,25 @@ fn resolveCallingConventionValues(
1252312528
12524 const ret_ty = fn_info.return_type.toType();12529 const ret_ty = fn_info.return_type.toType();
1252512530
12531 const resolved_cc = abi.resolveCallingConvention(cc, self.target.*);
12526 switch (cc) {12532 switch (cc) {
12527 .Naked => {12533 .Naked => {
12528 assert(result.args.len == 0);12534 assert(result.args.len == 0);
12529 result.return_value = InstTracking.init(.unreach);12535 result.return_value = InstTracking.init(.unreach);
12530 result.stack_align = .@"8";12536 result.stack_align = .@"8";
12531 },12537 },
12532 .C => {12538 .C, .SysV, .Win64 => {
12533 var param_reg_i: usize = 0;12539 var param_reg_i: usize = 0;
12534 var param_sse_reg_i: usize = 0;12540 var param_sse_reg_i: usize = 0;
12535 result.stack_align = .@"16";12541 result.stack_align = .@"16";
1253612542
12537 switch (self.target.os.tag) {12543 switch (resolved_cc) {
12538 .windows => {12544 .SysV => {},
12545 .Win64 => {
12539 // Align the stack to 16bytes before allocating shadow stack space (if any).12546 // Align the stack to 16bytes before allocating shadow stack space (if any).
12540 result.stack_byte_count += @intCast(4 * Type.usize.abiSize(mod));12547 result.stack_byte_count += @intCast(4 * Type.usize.abiSize(mod));
12541 },12548 },
12542 else => {},12549 else => unreachable,
12543 }12550 }
1254412551
12545 // Return values12552 // Return values
...@@ -12549,13 +12556,14 @@ fn resolveCallingConventionValues(...@@ -12549,13 +12556,14 @@ fn resolveCallingConventionValues(
12549 // TODO: is this even possible for C calling convention?12556 // TODO: is this even possible for C calling convention?
12550 result.return_value = InstTracking.init(.none);12557 result.return_value = InstTracking.init(.none);
12551 } else {12558 } else {
12552 const classes = switch (self.target.os.tag) {12559 const classes = switch (resolved_cc) {
12553 .windows => &[1]abi.Class{abi.classifyWindows(ret_ty, mod)},12560 .SysV => mem.sliceTo(&abi.classifySystemV(ret_ty, mod, .ret), .none),
12554 else => mem.sliceTo(&abi.classifySystemV(ret_ty, mod, .ret), .none),12561 .Win64 => &[1]abi.Class{abi.classifyWindows(ret_ty, mod)},
12562 else => unreachable,
12555 };12563 };
12556 for (12564 for (
12557 classes,12565 classes,
12558 abi.getCAbiIntReturnRegs(self.target.*)[0..classes.len],12566 abi.getCAbiIntReturnRegs(resolved_cc)[0..classes.len],
12559 0..,12567 0..,
12560 ) |class, ret_reg, ret_reg_i| {12568 ) |class, ret_reg, ret_reg_i| {
12561 result.return_value = switch (classes[0]) {12569 result.return_value = switch (classes[0]) {
...@@ -12577,7 +12585,7 @@ fn resolveCallingConventionValues(...@@ -12577,7 +12585,7 @@ fn resolveCallingConventionValues(
12577 .memory => switch (ret_reg_i) {12585 .memory => switch (ret_reg_i) {
12578 0 => ret: {12586 0 => ret: {
12579 const ret_indirect_reg =12587 const ret_indirect_reg =
12580 abi.getCAbiIntParamRegs(self.target.*)[param_reg_i];12588 abi.getCAbiIntParamRegs(resolved_cc)[param_reg_i];
12581 param_reg_i += 1;12589 param_reg_i += 1;
12582 break :ret .{12590 break :ret .{
12583 .short = .{ .indirect = .{ .reg = ret_reg } },12591 .short = .{ .indirect = .{ .reg = ret_reg } },
...@@ -12603,8 +12611,8 @@ fn resolveCallingConventionValues(...@@ -12603,8 +12611,8 @@ fn resolveCallingConventionValues(
12603 };12611 };
12604 for (classes, 0..) |class, class_i| {12612 for (classes, 0..) |class, class_i| {
12605 switch (class) {12613 switch (class) {
12606 .integer => if (param_reg_i < abi.getCAbiIntParamRegs(self.target.*).len) {12614 .integer => if (param_reg_i < abi.getCAbiIntParamRegs(resolved_cc).len) {
12607 const param_reg = abi.getCAbiIntParamRegs(self.target.*)[param_reg_i];12615 const param_reg = abi.getCAbiIntParamRegs(resolved_cc)[param_reg_i];
12608 param_reg_i += 1;12616 param_reg_i += 1;
1260912617
12610 arg.* = switch (class_i) {12618 arg.* = switch (class_i) {
...@@ -12661,13 +12669,13 @@ fn resolveCallingConventionValues(...@@ -12661,13 +12669,13 @@ fn resolveCallingConventionValues(
12661 } else if (!ret_ty.hasRuntimeBitsIgnoreComptime(mod)) {12669 } else if (!ret_ty.hasRuntimeBitsIgnoreComptime(mod)) {
12662 result.return_value = InstTracking.init(.none);12670 result.return_value = InstTracking.init(.none);
12663 } else {12671 } else {
12664 const ret_reg = abi.getCAbiIntReturnRegs(self.target.*)[0];12672 const ret_reg = abi.getCAbiIntReturnRegs(resolved_cc)[0];
12665 const ret_ty_size: u31 = @intCast(ret_ty.abiSize(mod));12673 const ret_ty_size: u31 = @intCast(ret_ty.abiSize(mod));
12666 if (ret_ty_size <= 8 and !ret_ty.isRuntimeFloat()) {12674 if (ret_ty_size <= 8 and !ret_ty.isRuntimeFloat()) {
12667 const aliased_reg = registerAlias(ret_reg, ret_ty_size);12675 const aliased_reg = registerAlias(ret_reg, ret_ty_size);
12668 result.return_value = .{ .short = .{ .register = aliased_reg }, .long = .none };12676 result.return_value = .{ .short = .{ .register = aliased_reg }, .long = .none };
12669 } else {12677 } else {
12670 const ret_indirect_reg = abi.getCAbiIntParamRegs(self.target.*)[0];12678 const ret_indirect_reg = abi.getCAbiIntParamRegs(resolved_cc)[0];
12671 result.return_value = .{12679 result.return_value = .{
12672 .short = .{ .indirect = .{ .reg = ret_reg } },12680 .short = .{ .indirect = .{ .reg = ret_reg } },
12673 .long = .{ .indirect = .{ .reg = ret_indirect_reg } },12681 .long = .{ .indirect = .{ .reg = ret_indirect_reg } },
src/arch/x86_64/Lower.zig+3-5
...@@ -2,7 +2,7 @@...@@ -2,7 +2,7 @@
22
3allocator: Allocator,3allocator: Allocator,
4mir: Mir,4mir: Mir,
5target: *const std.Target,5cc: std.builtin.CallingConvention,
6err_msg: ?*ErrorMsg = null,6err_msg: ?*ErrorMsg = null,
7src_loc: Module.SrcLoc,7src_loc: Module.SrcLoc,
8result_insts_len: u8 = undefined,8result_insts_len: u8 = undefined,
...@@ -552,15 +552,13 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void {...@@ -552,15 +552,13 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
552}552}
553553
554fn pushPopRegList(lower: *Lower, comptime mnemonic: Mnemonic, inst: Mir.Inst) Error!void {554fn pushPopRegList(lower: *Lower, comptime mnemonic: Mnemonic, inst: Mir.Inst) Error!void {
555 const callee_preserved_regs = abi.getCalleePreservedRegs(lower.target.*);555 const callee_preserved_regs = abi.getCalleePreservedRegs(lower.cc);
556 var it = inst.data.reg_list.iterator(.{ .direction = switch (mnemonic) {556 var it = inst.data.reg_list.iterator(.{ .direction = switch (mnemonic) {
557 .push => .reverse,557 .push => .reverse,
558 .pop => .forward,558 .pop => .forward,
559 else => unreachable,559 else => unreachable,
560 } });560 } });
561 while (it.next()) |i| try lower.emit(.none, mnemonic, &.{.{561 while (it.next()) |i| try lower.emit(.none, mnemonic, &.{.{ .reg = callee_preserved_regs[i] }});
562 .reg = callee_preserved_regs[i],
563 }});
564}562}
565563
566const page_size: i32 = 1 << 12;564const page_size: i32 = 1 << 12;
src/arch/x86_64/abi.zig+33-17
...@@ -463,31 +463,48 @@ pub const Win64 = struct {...@@ -463,31 +463,48 @@ pub const Win64 = struct {
463 pub const c_abi_int_return_regs = [_]Register{.rax};463 pub const c_abi_int_return_regs = [_]Register{.rax};
464};464};
465465
466pub fn getCalleePreservedRegs(target: Target) []const Register {466pub fn resolveCallingConvention(
467 return switch (target.os.tag) {467 cc: std.builtin.CallingConvention,
468 .windows => &Win64.callee_preserved_regs,468 target: std.Target,
469 else => &SysV.callee_preserved_regs,469) std.builtin.CallingConvention {
470 return switch (cc) {
471 .Unspecified, .C => switch (target.os.tag) {
472 else => .SysV,
473 .windows => .Win64,
474 },
475 else => cc,
476 };
477}
478
479pub fn getCalleePreservedRegs(cc: std.builtin.CallingConvention) []const Register {
480 return switch (cc) {
481 .SysV => &SysV.callee_preserved_regs,
482 .Win64 => &Win64.callee_preserved_regs,
483 else => unreachable,
470 };484 };
471}485}
472486
473pub fn getCallerPreservedRegs(target: Target) []const Register {487pub fn getCallerPreservedRegs(cc: std.builtin.CallingConvention) []const Register {
474 return switch (target.os.tag) {488 return switch (cc) {
475 .windows => &Win64.caller_preserved_regs,489 .SysV => &SysV.caller_preserved_regs,
476 else => &SysV.caller_preserved_regs,490 .Win64 => &Win64.caller_preserved_regs,
491 else => unreachable,
477 };492 };
478}493}
479494
480pub fn getCAbiIntParamRegs(target: Target) []const Register {495pub fn getCAbiIntParamRegs(cc: std.builtin.CallingConvention) []const Register {
481 return switch (target.os.tag) {496 return switch (cc) {
482 .windows => &Win64.c_abi_int_param_regs,497 .SysV => &SysV.c_abi_int_param_regs,
483 else => &SysV.c_abi_int_param_regs,498 .Win64 => &Win64.c_abi_int_param_regs,
499 else => unreachable,
484 };500 };
485}501}
486502
487pub fn getCAbiIntReturnRegs(target: Target) []const Register {503pub fn getCAbiIntReturnRegs(cc: std.builtin.CallingConvention) []const Register {
488 return switch (target.os.tag) {504 return switch (cc) {
489 .windows => &Win64.c_abi_int_return_regs,505 .SysV => &SysV.c_abi_int_return_regs,
490 else => &SysV.c_abi_int_return_regs,506 .Win64 => &Win64.c_abi_int_return_regs,
507 else => unreachable,
491 };508 };
492}509}
493510
...@@ -524,7 +541,6 @@ pub const RegisterClass = struct {...@@ -524,7 +541,6 @@ pub const RegisterClass = struct {
524541
525const builtin = @import("builtin");542const builtin = @import("builtin");
526const std = @import("std");543const std = @import("std");
527const Target = std.Target;
528const assert = std.debug.assert;544const assert = std.debug.assert;
529const testing = std.testing;545const testing = std.testing;
530546