x86_64: add support for Win64/C calling convention

This commit is contained in:
Jakub Konka
2022-09-01 12:24:55 +02:00
parent aac4c1d3b2
commit a19e6adbf9
4 changed files with 101 additions and 61 deletions

View File

@@ -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 {

View File

@@ -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),

View File

@@ -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(),
pub const RegisterList = struct {
bitset: BitSet = BitSet.initEmpty(),
const RegBitSet = IntegerBitSet(registers.len);
const Self = @This();
const BitSet = IntegerBitSet(@ctz(@as(u32, 0)));
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!
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 push(self: *Self, reg: Reg) void {
const index = getIndexForReg(reg);
self.bitset.set(index);
}
pub fn push(self: *Self, registers: []const Register, reg: Register) void {
const index = getIndexForReg(registers, reg);
self.bitset.set(index);
}
pub fn isSet(self: Self, reg: Reg) bool {
const index = getIndexForReg(reg);
return self.bitset.isSet(index);
}
pub fn isSet(self: Self, registers: []const Register, reg: Register) bool {
const index = getIndexForReg(registers, reg);
return self.bitset.isSet(index);
}
pub fn asInt(self: Self) u32 {
return self.bitset.mask;
}
pub fn asInt(self: Self) u32 {
return self.bitset.mask;
}
pub fn fromInt(mask: u32) Self {
return .{
.bitset = RegBitSet{ .mask = @intCast(RegBitSet.MaskInt, 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 fn count(self: Self) u32 {
return @intCast(u32, self.bitset.count());
}
};
pub const SaveRegisterList = struct {
/// Use `RegisterList` to populate.

View File

@@ -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;
};