aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJakub Konka <kubkon@jakubkonka.com>2022-09-01 12:24:55 +0200
committerJakub Konka <kubkon@jakubkonka.com>2022-09-07 22:42:56 +0200
commita19e6adbf90771890ecdbb52d6dafab1943e4cc4 (patch)
tree1361fa3ca3a41172b80dfd26fc7ee00700a0f4e5 /src
parentaac4c1d3b225ff4cd7138d9aae599c9540c7f04e (diff)
downloadzig-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.zig20
-rw-r--r--src/arch/x86_64/Emit.zig7
-rw-r--r--src/arch/x86_64/Mir.zig65
-rw-r--r--src/arch/x86_64/abi.zig74
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;
};