authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-09-01 12:24:55+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-09-07 22:42:56+02:00
loga19e6adbf90771890ecdbb52d6dafab1943e4cc4
tree1361fa3ca3a41172b80dfd26fc7ee00700a0f4e5
parentaac4c1d3b225ff4cd7138d9aae599c9540c7f04e

x86_64: add support for Win64/C calling convention


4 files changed, 103 insertions(+), 63 deletions(-)

src/arch/x86_64/CodeGen.zig+8-12
...@@ -32,11 +32,6 @@ const abi = @import("abi.zig");...@@ -32,11 +32,6 @@ const abi = @import("abi.zig");
32const errUnionPayloadOffset = codegen.errUnionPayloadOffset;32const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
33const errUnionErrorOffset = codegen.errUnionErrorOffset;33const errUnionErrorOffset = codegen.errUnionErrorOffset;
3434
35const callee_preserved_regs = abi.callee_preserved_regs;
36const caller_preserved_regs = abi.caller_preserved_regs;
37const c_abi_int_param_regs = abi.c_abi_int_param_regs;
38const c_abi_int_return_regs = abi.c_abi_int_return_regs;
39
40const Condition = bits.Condition;35const Condition = bits.Condition;
41const RegisterManager = abi.RegisterManager;36const RegisterManager = abi.RegisterManager;
42const RegisterLock = RegisterManager.RegisterLock;37const RegisterLock = RegisterManager.RegisterLock;
...@@ -448,10 +443,11 @@ fn gen(self: *Self) InnerError!void {...@@ -448,10 +443,11 @@ fn gen(self: *Self) InnerError!void {
448443
449 // Create list of registers to save in the prologue.444 // Create list of registers to save in the prologue.
450 // TODO handle register classes445 // TODO handle register classes
451 var reg_list: Mir.RegisterList(Register, &callee_preserved_regs) = .{};446 var reg_list = Mir.RegisterList{};
452 inline for (callee_preserved_regs) |reg| {447 const callee_preserved_regs = abi.getCalleePreservedRegs(self.target.*);
448 for (callee_preserved_regs) |reg| {
453 if (self.register_manager.isRegAllocated(reg)) {449 if (self.register_manager.isRegAllocated(reg)) {
454 reg_list.push(reg);450 reg_list.push(callee_preserved_regs, reg);
455 }451 }
456 }452 }
457 const saved_regs_stack_space: u32 = reg_list.count() * 8;453 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....@@ -3923,7 +3919,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
39233919
3924 try self.spillEflagsIfOccupied();3920 try self.spillEflagsIfOccupied();
39253921
3926 for (caller_preserved_regs) |reg| {3922 for (abi.getCallerPreservedRegs(self.target.*)) |reg| {
3927 try self.register_manager.getReg(reg, null);3923 try self.register_manager.getReg(reg, null);
3928 }3924 }
39293925
...@@ -7140,7 +7136,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {...@@ -7140,7 +7136,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
7140 assert(ret_ty.isError());7136 assert(ret_ty.isError());
7141 result.return_value = .{ .immediate = 0 };7137 result.return_value = .{ .immediate = 0 };
7142 } else if (ret_ty_size <= 8) {7138 } else if (ret_ty_size <= 8) {
7143 const aliased_reg = registerAlias(c_abi_int_return_regs[0], ret_ty_size);7139 const aliased_reg = registerAlias(abi.getCAbiIntReturnRegs(self.target.*)[0], ret_ty_size);
7144 result.return_value = .{ .register = aliased_reg };7140 result.return_value = .{ .register = aliased_reg };
7145 } else {7141 } else {
7146 // We simply make the return MCValue a stack offset. However, the actual value7142 // 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 {...@@ -7187,7 +7183,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
7187 else => false,7183 else => false,
7188 };7184 };
7189 if (pass_in_reg) {7185 if (pass_in_reg) {
7190 if (next_int_reg >= c_abi_int_param_regs.len) break;7186 if (next_int_reg >= abi.getCAbiIntParamRegs(self.target.*).len) break;
7191 try by_reg.putNoClobber(i, next_int_reg);7187 try by_reg.putNoClobber(i, next_int_reg);
7192 next_int_reg += 1;7188 next_int_reg += 1;
7193 }7189 }
...@@ -7210,7 +7206,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {...@@ -7210,7 +7206,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
7210 const param_size = @intCast(u32, ty.abiSize(self.target.*));7206 const param_size = @intCast(u32, ty.abiSize(self.target.*));
7211 const param_align = @intCast(u32, ty.abiAlignment(self.target.*));7207 const param_align = @intCast(u32, ty.abiAlignment(self.target.*));
7212 if (by_reg.get(i)) |int_reg| {7208 if (by_reg.get(i)) |int_reg| {
7213 const aliased_reg = registerAlias(c_abi_int_param_regs[int_reg], param_size);7209 const aliased_reg = registerAlias(abi.getCAbiIntParamRegs(self.target.*)[int_reg], param_size);
7214 result.args[i] = .{ .register = aliased_reg };7210 result.args[i] = .{ .register = aliased_reg };
7215 next_int_reg += 1;7211 next_int_reg += 1;
7216 } else {7212 } else {
src/arch/x86_64/Emit.zig+4-3
...@@ -283,10 +283,11 @@ fn mirPushPopRegisterList(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerErro...@@ -283,10 +283,11 @@ fn mirPushPopRegisterList(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerErro
283 const ops = emit.mir.instructions.items(.ops)[inst].decode();283 const ops = emit.mir.instructions.items(.ops)[inst].decode();
284 const payload = emit.mir.instructions.items(.data)[inst].payload;284 const payload = emit.mir.instructions.items(.data)[inst].payload;
285 const save_reg_list = emit.mir.extraData(Mir.SaveRegisterList, payload).data;285 const save_reg_list = emit.mir.extraData(Mir.SaveRegisterList, payload).data;
286 const reg_list = Mir.RegisterList(Register, &abi.callee_preserved_regs).fromInt(save_reg_list.register_list);
287 var disp: i32 = -@intCast(i32, save_reg_list.stack_end);286 var disp: i32 = -@intCast(i32, save_reg_list.stack_end);
288 inline for (abi.callee_preserved_regs) |reg| {287 const reg_list = Mir.RegisterList.fromInt(save_reg_list.register_list);
289 if (reg_list.isSet(reg)) {288 const callee_preserved_regs = abi.getCalleePreservedRegs(emit.target.*);
289 for (callee_preserved_regs) |reg| {
290 if (reg_list.isSet(callee_preserved_regs, reg)) {
290 switch (tag) {291 switch (tag) {
291 .push => try lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{292 .push => try lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{
292 .disp = @bitCast(u32, disp),293 .disp = @bitCast(u32, disp),
src/arch/x86_64/Mir.zig+31-34
...@@ -461,46 +461,43 @@ pub const Inst = struct {...@@ -461,46 +461,43 @@ pub const Inst = struct {
461 }461 }
462};462};
463463
464pub fn RegisterList(comptime Reg: type, comptime registers: []const Reg) type {464pub const RegisterList = struct {
465 assert(registers.len <= @bitSizeOf(u32));465 bitset: BitSet = BitSet.initEmpty(),
466 return struct {
467 bitset: RegBitSet = RegBitSet.initEmpty(),
468
469 const RegBitSet = IntegerBitSet(registers.len);
470 const Self = @This();
471
472 fn getIndexForReg(reg: Reg) RegBitSet.MaskInt {
473 inline for (registers) |cpreg, i| {
474 if (reg.id() == cpreg.id()) return i;
475 }
476 unreachable; // register not in input register list!
477 }
478466
479 pub fn push(self: *Self, reg: Reg) void {467 const BitSet = IntegerBitSet(@ctz(@as(u32, 0)));
480 const index = getIndexForReg(reg);468 const Self = @This();
481 self.bitset.set(index);
482 }
483469
484 pub fn isSet(self: Self, reg: Reg) bool {470 fn getIndexForReg(registers: []const Register, reg: Register) BitSet.MaskInt {
485 const index = getIndexForReg(reg);471 for (registers) |cpreg, i| {
486 return self.bitset.isSet(index);472 if (reg.id() == cpreg.id()) return @intCast(u32, i);
487 }473 }
474 unreachable; // register not in input register list!
475 }
488476
489 pub fn asInt(self: Self) u32 {477 pub fn push(self: *Self, registers: []const Register, reg: Register) void {
490 return self.bitset.mask;478 const index = getIndexForReg(registers, reg);
491 }479 self.bitset.set(index);
480 }
492481
493 pub fn fromInt(mask: u32) Self {482 pub fn isSet(self: Self, registers: []const Register, reg: Register) bool {
494 return .{483 const index = getIndexForReg(registers, reg);
495 .bitset = RegBitSet{ .mask = @intCast(RegBitSet.MaskInt, mask) },484 return self.bitset.isSet(index);
496 };485 }
497 }
498486
499 pub fn count(self: Self) u32 {487 pub fn asInt(self: Self) u32 {
500 return @intCast(u32, self.bitset.count());488 return self.bitset.mask;
501 }489 }
502 };490
503}491 pub fn fromInt(mask: u32) Self {
492 return .{
493 .bitset = BitSet{ .mask = @intCast(BitSet.MaskInt, mask) },
494 };
495 }
496
497 pub fn count(self: Self) u32 {
498 return @intCast(u32, self.bitset.count());
499 }
500};
504501
505pub const SaveRegisterList = struct {502pub const SaveRegisterList = struct {
506 /// Use `RegisterList` to populate.503 /// Use `RegisterList` to populate.
src/arch/x86_64/abi.zig+60-14
...@@ -392,23 +392,69 @@ pub fn classifySystemV(ty: Type, target: Target) [8]Class {...@@ -392,23 +392,69 @@ pub fn classifySystemV(ty: Type, target: Target) [8]Class {
392 }392 }
393}393}
394394
395/// Note that .rsp and .rbp also belong to this set, however, we never expect to use them395pub const SysV = struct {
396/// for anything else but stack offset tracking therefore we exclude them from this set.396 /// Note that .rsp and .rbp also belong to this set, however, we never expect to use them
397pub const callee_preserved_regs = [_]Register{ .rbx, .r12, .r13, .r14, .r15 };397 /// for anything else but stack offset tracking therefore we exclude them from this set.
398/// These registers need to be preserved (saved on the stack) and restored by the caller before398 pub const callee_preserved_regs = [_]Register{ .rbx, .r12, .r13, .r14, .r15 };
399/// the caller relinquishes control to a subroutine via call instruction (or similar).399 /// These registers need to be preserved (saved on the stack) and restored by the caller before
400/// In other words, these registers are free to use by the callee.400 /// the caller relinquishes control to a subroutine via call instruction (or similar).
401pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 };401 /// In other words, these registers are free to use by the callee.
402 pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 };
402403
403pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };404 pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
404pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx };405 pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx };
406};
407
408pub const Win64 = struct {
409 /// Note that .rsp and .rbp also belong to this set, however, we never expect to use them
410 /// for anything else but stack offset tracking therefore we exclude them from this set.
411 pub const callee_preserved_regs = [_]Register{ .rbx, .rsi, .rdi, .r12, .r13, .r14, .r15 };
412 /// These registers need to be preserved (saved on the stack) and restored by the caller before
413 /// the caller relinquishes control to a subroutine via call instruction (or similar).
414 /// In other words, these registers are free to use by the callee.
415 pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .r8, .r9, .r10, .r11 };
405416
417 pub const c_abi_int_param_regs = [_]Register{ .rcx, .rdx, .r8, .r9 };
418 pub const c_abi_int_return_regs = [_]Register{.rax};
419};
420
421pub fn getCalleePreservedRegs(target: Target) []const Register {
422 return switch (target.os.tag) {
423 .windows => &Win64.callee_preserved_regs,
424 else => &SysV.callee_preserved_regs,
425 };
426}
427
428pub fn getCallerPreservedRegs(target: Target) []const Register {
429 return switch (target.os.tag) {
430 .windows => &Win64.caller_preserved_regs,
431 else => &SysV.caller_preserved_regs,
432 };
433}
434
435pub fn getCAbiIntParamRegs(target: Target) []const Register {
436 return switch (target.os.tag) {
437 .windows => &Win64.c_abi_int_param_regs,
438 else => &SysV.c_abi_int_param_regs,
439 };
440}
441
442pub fn getCAbiIntReturnRegs(target: Target) []const Register {
443 return switch (target.os.tag) {
444 .windows => &Win64.c_abi_int_return_regs,
445 else => &SysV.c_abi_int_return_regs,
446 };
447}
448
449const gp_regs = [_]Register{
450 .rax, .rbx, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11, .r12, .r13, .r14, .r15,
451};
406const sse_avx_regs = [_]Register{452const sse_avx_regs = [_]Register{
407 .ymm0, .ymm1, .ymm2, .ymm3, .ymm4, .ymm5, .ymm6, .ymm7,453 .ymm0, .ymm1, .ymm2, .ymm3, .ymm4, .ymm5, .ymm6, .ymm7,
408 .ymm8, .ymm9, .ymm10, .ymm11, .ymm12, .ymm13, .ymm14, .ymm15,454 .ymm8, .ymm9, .ymm10, .ymm11, .ymm12, .ymm13, .ymm14, .ymm15,
409};455};
410const allocatable_registers = callee_preserved_regs ++ caller_preserved_regs ++ sse_avx_regs;456const allocatable_regs = gp_regs ++ sse_avx_regs;
411pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_registers);457pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_regs);
412458
413// Register classes459// Register classes
414const RegisterBitSet = RegisterManager.RegisterBitSet;460const RegisterBitSet = RegisterManager.RegisterBitSet;
...@@ -417,15 +463,15 @@ pub const RegisterClass = struct {...@@ -417,15 +463,15 @@ pub const RegisterClass = struct {
417 var set = RegisterBitSet.initEmpty();463 var set = RegisterBitSet.initEmpty();
418 set.setRangeValue(.{464 set.setRangeValue(.{
419 .start = 0,465 .start = 0,
420 .end = caller_preserved_regs.len + callee_preserved_regs.len,466 .end = gp_regs.len,
421 }, true);467 }, true);
422 break :blk set;468 break :blk set;
423 };469 };
424 pub const sse: RegisterBitSet = blk: {470 pub const sse: RegisterBitSet = blk: {
425 var set = RegisterBitSet.initEmpty();471 var set = RegisterBitSet.initEmpty();
426 set.setRangeValue(.{472 set.setRangeValue(.{
427 .start = caller_preserved_regs.len + callee_preserved_regs.len,473 .start = gp_regs.len,
428 .end = allocatable_registers.len,474 .end = allocatable_regs.len,
429 }, true);475 }, true);
430 break :blk set;476 break :blk set;
431 };477 };