x86_64: add support for Win64/C calling convention
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user