diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2022-09-01 12:24:55 +0200 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2022-09-07 22:42:56 +0200 |
| commit | a19e6adbf90771890ecdbb52d6dafab1943e4cc4 (patch) | |
| tree | 1361fa3ca3a41172b80dfd26fc7ee00700a0f4e5 /src | |
| parent | aac4c1d3b225ff4cd7138d9aae599c9540c7f04e (diff) | |
| download | zig-a19e6adbf90771890ecdbb52d6dafab1943e4cc4.tar.gz zig-a19e6adbf90771890ecdbb52d6dafab1943e4cc4.zip | |
x86_64: add support for Win64/C calling convention
Diffstat (limited to 'src')
| -rw-r--r-- | src/arch/x86_64/CodeGen.zig | 20 | ||||
| -rw-r--r-- | src/arch/x86_64/Emit.zig | 7 | ||||
| -rw-r--r-- | src/arch/x86_64/Mir.zig | 65 | ||||
| -rw-r--r-- | src/arch/x86_64/abi.zig | 74 |
4 files changed, 103 insertions, 63 deletions
diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index e6386d3ac7..5e404d00bd 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -32,11 +32,6 @@ const abi = @import("abi.zig"); const errUnionPayloadOffset = codegen.errUnionPayloadOffset; const errUnionErrorOffset = codegen.errUnionErrorOffset; -const callee_preserved_regs = abi.callee_preserved_regs; -const caller_preserved_regs = abi.caller_preserved_regs; -const c_abi_int_param_regs = abi.c_abi_int_param_regs; -const c_abi_int_return_regs = abi.c_abi_int_return_regs; - const Condition = bits.Condition; const RegisterManager = abi.RegisterManager; const RegisterLock = RegisterManager.RegisterLock; @@ -448,10 +443,11 @@ fn gen(self: *Self) InnerError!void { // Create list of registers to save in the prologue. // TODO handle register classes - var reg_list: Mir.RegisterList(Register, &callee_preserved_regs) = .{}; - inline for (callee_preserved_regs) |reg| { + var reg_list = Mir.RegisterList{}; + const callee_preserved_regs = abi.getCalleePreservedRegs(self.target.*); + for (callee_preserved_regs) |reg| { if (self.register_manager.isRegAllocated(reg)) { - reg_list.push(reg); + reg_list.push(callee_preserved_regs, reg); } } const saved_regs_stack_space: u32 = reg_list.count() * 8; @@ -3923,7 +3919,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. try self.spillEflagsIfOccupied(); - for (caller_preserved_regs) |reg| { + for (abi.getCallerPreservedRegs(self.target.*)) |reg| { try self.register_manager.getReg(reg, null); } @@ -7140,7 +7136,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { assert(ret_ty.isError()); result.return_value = .{ .immediate = 0 }; } else if (ret_ty_size <= 8) { - const aliased_reg = registerAlias(c_abi_int_return_regs[0], ret_ty_size); + const aliased_reg = registerAlias(abi.getCAbiIntReturnRegs(self.target.*)[0], ret_ty_size); result.return_value = .{ .register = aliased_reg }; } else { // We simply make the return MCValue a stack offset. However, the actual value @@ -7187,7 +7183,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { else => false, }; if (pass_in_reg) { - if (next_int_reg >= c_abi_int_param_regs.len) break; + if (next_int_reg >= abi.getCAbiIntParamRegs(self.target.*).len) break; try by_reg.putNoClobber(i, next_int_reg); next_int_reg += 1; } @@ -7210,7 +7206,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { const param_size = @intCast(u32, ty.abiSize(self.target.*)); const param_align = @intCast(u32, ty.abiAlignment(self.target.*)); if (by_reg.get(i)) |int_reg| { - const aliased_reg = registerAlias(c_abi_int_param_regs[int_reg], param_size); + const aliased_reg = registerAlias(abi.getCAbiIntParamRegs(self.target.*)[int_reg], param_size); result.args[i] = .{ .register = aliased_reg }; next_int_reg += 1; } else { diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index a0f34d5732..66e603aab0 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -283,10 +283,11 @@ fn mirPushPopRegisterList(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerErro const ops = emit.mir.instructions.items(.ops)[inst].decode(); const payload = emit.mir.instructions.items(.data)[inst].payload; const save_reg_list = emit.mir.extraData(Mir.SaveRegisterList, payload).data; - const reg_list = Mir.RegisterList(Register, &abi.callee_preserved_regs).fromInt(save_reg_list.register_list); var disp: i32 = -@intCast(i32, save_reg_list.stack_end); - inline for (abi.callee_preserved_regs) |reg| { - if (reg_list.isSet(reg)) { + const reg_list = Mir.RegisterList.fromInt(save_reg_list.register_list); + const callee_preserved_regs = abi.getCalleePreservedRegs(emit.target.*); + for (callee_preserved_regs) |reg| { + if (reg_list.isSet(callee_preserved_regs, reg)) { switch (tag) { .push => try lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{ .disp = @bitCast(u32, disp), diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index b2e0f204eb..ca19847042 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -461,46 +461,43 @@ pub const Inst = struct { } }; -pub fn RegisterList(comptime Reg: type, comptime registers: []const Reg) type { - assert(registers.len <= @bitSizeOf(u32)); - return struct { - bitset: RegBitSet = RegBitSet.initEmpty(), - - const RegBitSet = IntegerBitSet(registers.len); - const Self = @This(); - - fn getIndexForReg(reg: Reg) RegBitSet.MaskInt { - inline for (registers) |cpreg, i| { - if (reg.id() == cpreg.id()) return i; - } - unreachable; // register not in input register list! - } +pub const RegisterList = struct { + bitset: BitSet = BitSet.initEmpty(), - pub fn push(self: *Self, reg: Reg) void { - const index = getIndexForReg(reg); - self.bitset.set(index); - } + const BitSet = IntegerBitSet(@ctz(@as(u32, 0))); + const Self = @This(); - pub fn isSet(self: Self, reg: Reg) bool { - const index = getIndexForReg(reg); - return self.bitset.isSet(index); + fn getIndexForReg(registers: []const Register, reg: Register) BitSet.MaskInt { + for (registers) |cpreg, i| { + if (reg.id() == cpreg.id()) return @intCast(u32, i); } + unreachable; // register not in input register list! + } - pub fn asInt(self: Self) u32 { - return self.bitset.mask; - } + pub fn push(self: *Self, registers: []const Register, reg: Register) void { + const index = getIndexForReg(registers, reg); + self.bitset.set(index); + } - pub fn fromInt(mask: u32) Self { - return .{ - .bitset = RegBitSet{ .mask = @intCast(RegBitSet.MaskInt, mask) }, - }; - } + pub fn isSet(self: Self, registers: []const Register, reg: Register) bool { + const index = getIndexForReg(registers, reg); + return self.bitset.isSet(index); + } - pub fn count(self: Self) u32 { - return @intCast(u32, self.bitset.count()); - } - }; -} + pub fn asInt(self: Self) u32 { + return self.bitset.mask; + } + + pub fn fromInt(mask: u32) Self { + return .{ + .bitset = BitSet{ .mask = @intCast(BitSet.MaskInt, mask) }, + }; + } + + pub fn count(self: Self) u32 { + return @intCast(u32, self.bitset.count()); + } +}; pub const SaveRegisterList = struct { /// Use `RegisterList` to populate. diff --git a/src/arch/x86_64/abi.zig b/src/arch/x86_64/abi.zig index 344fe235f3..2f4a7d1681 100644 --- a/src/arch/x86_64/abi.zig +++ b/src/arch/x86_64/abi.zig @@ -392,23 +392,69 @@ pub fn classifySystemV(ty: Type, target: Target) [8]Class { } } -/// Note that .rsp and .rbp also belong to this set, however, we never expect to use them -/// for anything else but stack offset tracking therefore we exclude them from this set. -pub const callee_preserved_regs = [_]Register{ .rbx, .r12, .r13, .r14, .r15 }; -/// These registers need to be preserved (saved on the stack) and restored by the caller before -/// the caller relinquishes control to a subroutine via call instruction (or similar). -/// In other words, these registers are free to use by the callee. -pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 }; +pub const SysV = struct { + /// Note that .rsp and .rbp also belong to this set, however, we never expect to use them + /// for anything else but stack offset tracking therefore we exclude them from this set. + pub const callee_preserved_regs = [_]Register{ .rbx, .r12, .r13, .r14, .r15 }; + /// These registers need to be preserved (saved on the stack) and restored by the caller before + /// the caller relinquishes control to a subroutine via call instruction (or similar). + /// In other words, these registers are free to use by the callee. + pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 }; -pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 }; -pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx }; + pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 }; + pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx }; +}; + +pub const Win64 = struct { + /// Note that .rsp and .rbp also belong to this set, however, we never expect to use them + /// for anything else but stack offset tracking therefore we exclude them from this set. + pub const callee_preserved_regs = [_]Register{ .rbx, .rsi, .rdi, .r12, .r13, .r14, .r15 }; + /// These registers need to be preserved (saved on the stack) and restored by the caller before + /// the caller relinquishes control to a subroutine via call instruction (or similar). + /// In other words, these registers are free to use by the callee. + pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .r8, .r9, .r10, .r11 }; + pub const c_abi_int_param_regs = [_]Register{ .rcx, .rdx, .r8, .r9 }; + pub const c_abi_int_return_regs = [_]Register{.rax}; +}; + +pub fn getCalleePreservedRegs(target: Target) []const Register { + return switch (target.os.tag) { + .windows => &Win64.callee_preserved_regs, + else => &SysV.callee_preserved_regs, + }; +} + +pub fn getCallerPreservedRegs(target: Target) []const Register { + return switch (target.os.tag) { + .windows => &Win64.caller_preserved_regs, + else => &SysV.caller_preserved_regs, + }; +} + +pub fn getCAbiIntParamRegs(target: Target) []const Register { + return switch (target.os.tag) { + .windows => &Win64.c_abi_int_param_regs, + else => &SysV.c_abi_int_param_regs, + }; +} + +pub fn getCAbiIntReturnRegs(target: Target) []const Register { + return switch (target.os.tag) { + .windows => &Win64.c_abi_int_return_regs, + else => &SysV.c_abi_int_return_regs, + }; +} + +const gp_regs = [_]Register{ + .rax, .rbx, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11, .r12, .r13, .r14, .r15, +}; const sse_avx_regs = [_]Register{ .ymm0, .ymm1, .ymm2, .ymm3, .ymm4, .ymm5, .ymm6, .ymm7, .ymm8, .ymm9, .ymm10, .ymm11, .ymm12, .ymm13, .ymm14, .ymm15, }; -const allocatable_registers = callee_preserved_regs ++ caller_preserved_regs ++ sse_avx_regs; -pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_registers); +const allocatable_regs = gp_regs ++ sse_avx_regs; +pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_regs); // Register classes const RegisterBitSet = RegisterManager.RegisterBitSet; @@ -417,15 +463,15 @@ pub const RegisterClass = struct { var set = RegisterBitSet.initEmpty(); set.setRangeValue(.{ .start = 0, - .end = caller_preserved_regs.len + callee_preserved_regs.len, + .end = gp_regs.len, }, true); break :blk set; }; pub const sse: RegisterBitSet = blk: { var set = RegisterBitSet.initEmpty(); set.setRangeValue(.{ - .start = caller_preserved_regs.len + callee_preserved_regs.len, - .end = allocatable_registers.len, + .start = gp_regs.len, + .end = allocatable_regs.len, }, true); break :blk set; }; |
