authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-03-29 18:08:03+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-03-30 00:37:42+02:00
log376d0878ec2e366633e2dbc7c91ab7ae2a6ae5b7
treed3c55c3acdd58283c85057cf7f351925f5e8969f
parentd447cd940d7da884f0d699d9da679d8bbabb237a

x64: spill .rdi to stack if expecting return value saved on stack

Since .rdi is not part of the callee saved registers, it needs to be proactively spilled to the stack so that we don't clobber the return address where to save the return value.

1 files changed, 45 insertions(+), 68 deletions(-)

src/arch/x86_64/CodeGen.zig+45-68
...@@ -58,7 +58,6 @@ arg_index: u32,...@@ -58,7 +58,6 @@ arg_index: u32,
58src_loc: Module.SrcLoc,58src_loc: Module.SrcLoc,
59stack_align: u32,59stack_align: u32,
6060
61ret_backpatches: std.ArrayListUnmanaged(Mir.Inst.Index) = .{},
62compare_flags_inst: ?Air.Inst.Index = null,61compare_flags_inst: ?Air.Inst.Index = null,
6362
64/// MIR Instructions63/// MIR Instructions
...@@ -353,7 +352,6 @@ pub fn generate(...@@ -353,7 +352,6 @@ pub fn generate(
353 std.AutoHashMap(Mir.Inst.Index, Air.Inst.Index).init(bin_file.allocator)352 std.AutoHashMap(Mir.Inst.Index, Air.Inst.Index).init(bin_file.allocator)
354 else {},353 else {},
355 };354 };
356 defer function.ret_backpatches.deinit(bin_file.allocator);
357 defer function.stack.deinit(bin_file.allocator);355 defer function.stack.deinit(bin_file.allocator);
358 defer function.blocks.deinit(bin_file.allocator);356 defer function.blocks.deinit(bin_file.allocator);
359 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);357 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
...@@ -482,6 +480,21 @@ fn gen(self: *Self) InnerError!void {...@@ -482,6 +480,21 @@ fn gen(self: *Self) InnerError!void {
482 .data = undefined,480 .data = undefined,
483 });481 });
484482
483 if (self.ret_mcv == .stack_offset) {
484 // The address where to store the return value for the caller is in `.rdi`
485 // register which the callee is free to clobber. Therefore, we purposely
486 // spill it to stack immediately.
487 const ptr_ty = Type.usize;
488 const abi_size = @intCast(u32, ptr_ty.abiSize(self.target.*));
489 const abi_align = ptr_ty.abiAlignment(self.target.*);
490 const stack_offset = mem.alignForwardGeneric(u32, self.next_stack_offset + abi_size, abi_align);
491 self.next_stack_offset = stack_offset;
492 self.max_end_stack = @maximum(self.max_end_stack, self.next_stack_offset);
493 try self.genSetStack(ptr_ty, @intCast(i32, stack_offset), MCValue{ .register = .rdi }, .{});
494 self.ret_mcv = MCValue{ .stack_offset = @intCast(i32, stack_offset) };
495 log.debug("gen: spilling .rdi to stack at offset {}", .{stack_offset});
496 }
497
485 _ = try self.addInst(.{498 _ = try self.addInst(.{
486 .tag = .dbg_prologue_end,499 .tag = .dbg_prologue_end,
487 .ops = undefined,500 .ops = undefined,
...@@ -519,23 +532,6 @@ fn gen(self: *Self) InnerError!void {...@@ -519,23 +532,6 @@ fn gen(self: *Self) InnerError!void {
519 var disp = data.disp + 8;532 var disp = data.disp + 8;
520 inline for (callee_preserved_regs) |reg, i| {533 inline for (callee_preserved_regs) |reg, i| {
521 if (self.register_manager.isRegAllocated(reg)) {534 if (self.register_manager.isRegAllocated(reg)) {
522 if (reg.to64() == .rdi) {
523 for (self.ret_backpatches.items) |inst| {
524 log.debug(".rdi was spilled, backpatching with mov from stack at offset {}", .{
525 -@intCast(i32, disp),
526 });
527 const ops = Mir.Ops.decode(self.mir_instructions.items(.ops)[inst]);
528 self.mir_instructions.set(inst, Mir.Inst{
529 .tag = .mov,
530 .ops = (Mir.Ops{
531 .reg1 = ops.reg1,
532 .reg2 = .rbp,
533 .flags = 0b01,
534 }).encode(),
535 .data = .{ .imm = @bitCast(u32, -@intCast(i32, disp)) },
536 });
537 }
538 }
539 data.regs |= 1 << @intCast(u5, i);535 data.regs |= 1 << @intCast(u5, i);
540 self.max_end_stack += 8;536 self.max_end_stack += 8;
541 disp += 8;537 disp += 8;
...@@ -912,8 +908,7 @@ fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u...@@ -912,8 +908,7 @@ fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u
912 // TODO find a free slot instead of always appending908 // TODO find a free slot instead of always appending
913 const offset = mem.alignForwardGeneric(u32, self.next_stack_offset + abi_size, abi_align);909 const offset = mem.alignForwardGeneric(u32, self.next_stack_offset + abi_size, abi_align);
914 self.next_stack_offset = offset;910 self.next_stack_offset = offset;
915 if (self.next_stack_offset > self.max_end_stack)911 self.max_end_stack = @maximum(self.max_end_stack, self.next_stack_offset);
916 self.max_end_stack = self.next_stack_offset;
917 try self.stack.putNoClobber(self.gpa, offset, .{912 try self.stack.putNoClobber(self.gpa, offset, .{
918 .inst = inst,913 .inst = inst,
919 .size = abi_size,914 .size = abi_size,
...@@ -3316,7 +3311,6 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC...@@ -3316,7 +3311,6 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC
3316}3311}
33173312
3318/// Performs multi-operand integer multiplication between dst_mcv and src_mcv, storing the result in dst_mcv.3313/// Performs multi-operand integer multiplication between dst_mcv and src_mcv, storing the result in dst_mcv.
3319/// Does not use/spill .rax/.rdx.
3320/// Does not support byte-size operands.3314/// Does not support byte-size operands.
3321fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void {3315fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void {
3322 const abi_size = @intCast(u32, dst_ty.abiSize(self.target.*));3316 const abi_size = @intCast(u32, dst_ty.abiSize(self.target.*));
...@@ -3530,10 +3524,11 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions....@@ -3530,10 +3524,11 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
3530 const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*));3524 const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*));
3531 const ret_abi_align = @intCast(u32, ret_ty.abiAlignment(self.target.*));3525 const ret_abi_align = @intCast(u32, ret_ty.abiAlignment(self.target.*));
3532 const stack_offset = @intCast(i32, try self.allocMem(inst, ret_abi_size, ret_abi_align));3526 const stack_offset = @intCast(i32, try self.allocMem(inst, ret_abi_size, ret_abi_align));
3527 log.debug("airCall: return value on stack at offset {}", .{stack_offset});
35333528
3534 try self.register_manager.getReg(.rdi, null);3529 try self.register_manager.getReg(.rdi, null);
3535 self.register_manager.freezeRegs(&.{.rdi});
3536 try self.genSetReg(Type.usize, .rdi, .{ .ptr_stack_offset = stack_offset });3530 try self.genSetReg(Type.usize, .rdi, .{ .ptr_stack_offset = stack_offset });
3531 self.register_manager.freezeRegs(&.{.rdi});
35373532
3538 info.return_value.stack_offset = stack_offset;3533 info.return_value.stack_offset = stack_offset;
3539 }3534 }
...@@ -3720,15 +3715,13 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions....@@ -3720,15 +3715,13 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
37203715
3721 const result: MCValue = result: {3716 const result: MCValue = result: {
3722 switch (info.return_value) {3717 switch (info.return_value) {
3723 .register => |reg| {3718 .register => {
3724 if (RegisterManager.indexOfRegIntoTracked(reg) == null) {3719 // Save function return value in a new register
3725 // Save function return value in a callee saved register3720 break :result try self.copyToRegisterWithInstTracking(
3726 break :result try self.copyToRegisterWithInstTracking(3721 inst,
3727 inst,3722 self.air.typeOfIndex(inst),
3728 self.air.typeOfIndex(inst),3723 info.return_value,
3729 info.return_value,3724 );
3730 );
3731 }
3732 },3725 },
3733 else => {},3726 else => {},
3734 }3727 }
...@@ -3755,19 +3748,11 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void {...@@ -3755,19 +3748,11 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void {
3755 const ret_ty = self.fn_type.fnReturnType();3748 const ret_ty = self.fn_type.fnReturnType();
3756 switch (self.ret_mcv) {3749 switch (self.ret_mcv) {
3757 .stack_offset => {3750 .stack_offset => {
3758 // TODO audit register allocation!3751 self.register_manager.freezeRegs(&.{ .rax, .rcx });
3759 self.register_manager.freezeRegs(&.{ .rax, .rcx, .rdi });3752 defer self.register_manager.unfreezeRegs(&.{ .rax, .rcx });
3760 defer self.register_manager.unfreezeRegs(&.{ .rax, .rcx, .rdi });3753 const reg = try self.copyToTmpRegister(Type.usize, self.ret_mcv);
3761 const reg = try self.register_manager.allocReg(null);3754 self.register_manager.freezeRegs(&.{reg});
3762 const backpatch = try self.addInst(.{3755 defer self.register_manager.unfreezeRegs(&.{reg});
3763 .tag = .mov,
3764 .ops = (Mir.Ops{
3765 .reg1 = reg,
3766 .reg2 = .rdi,
3767 }).encode(),
3768 .data = undefined,
3769 });
3770 try self.ret_backpatches.append(self.gpa, backpatch);
3771 try self.genSetStack(ret_ty, 0, operand, .{3756 try self.genSetStack(ret_ty, 0, operand, .{
3772 .source_stack_base = .rbp,3757 .source_stack_base = .rbp,
3773 .dest_stack_base = reg,3758 .dest_stack_base = reg,
...@@ -3798,19 +3783,11 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {...@@ -3798,19 +3783,11 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
3798 const elem_ty = ptr_ty.elemType();3783 const elem_ty = ptr_ty.elemType();
3799 switch (self.ret_mcv) {3784 switch (self.ret_mcv) {
3800 .stack_offset => {3785 .stack_offset => {
3801 // TODO audit register allocation!3786 self.register_manager.freezeRegs(&.{ .rax, .rcx });
3802 self.register_manager.freezeRegs(&.{ .rax, .rcx, .rdi });3787 defer self.register_manager.unfreezeRegs(&.{ .rax, .rcx });
3803 defer self.register_manager.unfreezeRegs(&.{ .rax, .rcx, .rdi });3788 const reg = try self.copyToTmpRegister(Type.usize, self.ret_mcv);
3804 const reg = try self.register_manager.allocReg(null);3789 self.register_manager.freezeRegs(&.{reg});
3805 const backpatch = try self.addInst(.{3790 defer self.register_manager.unfreezeRegs(&.{reg});
3806 .tag = .mov,
3807 .ops = (Mir.Ops{
3808 .reg1 = reg,
3809 .reg2 = .rdi,
3810 }).encode(),
3811 .data = undefined,
3812 });
3813 try self.ret_backpatches.append(self.gpa, backpatch);
3814 try self.genInlineMemcpy(.{ .stack_offset = 0 }, ptr, .{ .immediate = elem_ty.abiSize(self.target.*) }, .{3791 try self.genInlineMemcpy(.{ .stack_offset = 0 }, ptr, .{ .immediate = elem_ty.abiSize(self.target.*) }, .{
3815 .source_stack_base = .rbp,3792 .source_stack_base = .rbp,
3816 .dest_stack_base = reg,3793 .dest_stack_base = reg,
...@@ -5092,6 +5069,7 @@ const InlineMemcpyOpts = struct {...@@ -5092,6 +5069,7 @@ const InlineMemcpyOpts = struct {
5092 dest_stack_base: ?Register = null,5069 dest_stack_base: ?Register = null,
5093};5070};
50945071
5072/// Spills .rax and .rcx.
5095fn genInlineMemcpy(5073fn genInlineMemcpy(
5096 self: *Self,5074 self: *Self,
5097 dst_ptr: MCValue,5075 dst_ptr: MCValue,
...@@ -5099,13 +5077,7 @@ fn genInlineMemcpy(...@@ -5099,13 +5077,7 @@ fn genInlineMemcpy(
5099 len: MCValue,5077 len: MCValue,
5100 opts: InlineMemcpyOpts,5078 opts: InlineMemcpyOpts,
5101) InnerError!void {5079) InnerError!void {
5102 // TODO this is wrong. We should check first if any of the operands is in `.rax` or `.rcx` before
5103 // spilling. Consolidate with other TODOs regarding register allocation mechanics.
5104 try self.register_manager.getReg(.rax, null);
5105 try self.register_manager.getReg(.rcx, null);
5106
5107 self.register_manager.freezeRegs(&.{ .rax, .rcx });5080 self.register_manager.freezeRegs(&.{ .rax, .rcx });
5108 defer self.register_manager.unfreezeRegs(&.{ .rax, .rcx });
51095081
5110 if (opts.source_stack_base) |reg| self.register_manager.freezeRegs(&.{reg});5082 if (opts.source_stack_base) |reg| self.register_manager.freezeRegs(&.{reg});
5111 defer if (opts.source_stack_base) |reg| self.register_manager.unfreezeRegs(&.{reg});5083 defer if (opts.source_stack_base) |reg| self.register_manager.unfreezeRegs(&.{reg});
...@@ -5145,7 +5117,6 @@ fn genInlineMemcpy(...@@ -5145,7 +5117,6 @@ fn genInlineMemcpy(
5145 return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr});5117 return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr});
5146 },5118 },
5147 }5119 }
5148
5149 self.register_manager.freezeRegs(&.{dst_addr_reg});5120 self.register_manager.freezeRegs(&.{dst_addr_reg});
5150 defer self.register_manager.unfreezeRegs(&.{dst_addr_reg});5121 defer self.register_manager.unfreezeRegs(&.{dst_addr_reg});
51515122
...@@ -5181,7 +5152,6 @@ fn genInlineMemcpy(...@@ -5181,7 +5152,6 @@ fn genInlineMemcpy(
5181 return self.fail("TODO implement memcpy for setting stack when src is {}", .{src_ptr});5152 return self.fail("TODO implement memcpy for setting stack when src is {}", .{src_ptr});
5182 },5153 },
5183 }5154 }
5184
5185 self.register_manager.freezeRegs(&.{src_addr_reg});5155 self.register_manager.freezeRegs(&.{src_addr_reg});
5186 defer self.register_manager.unfreezeRegs(&.{src_addr_reg});5156 defer self.register_manager.unfreezeRegs(&.{src_addr_reg});
51875157
...@@ -5189,6 +5159,11 @@ fn genInlineMemcpy(...@@ -5189,6 +5159,11 @@ fn genInlineMemcpy(
5189 const count_reg = regs[0].to64();5159 const count_reg = regs[0].to64();
5190 const tmp_reg = regs[1].to8();5160 const tmp_reg = regs[1].to8();
51915161
5162 self.register_manager.unfreezeRegs(&.{ .rax, .rcx });
5163
5164 try self.register_manager.getReg(.rax, null);
5165 try self.register_manager.getReg(.rcx, null);
5166
5192 try self.genSetReg(Type.usize, count_reg, len);5167 try self.genSetReg(Type.usize, count_reg, len);
51935168
5194 // mov rcx, 05169 // mov rcx, 0
...@@ -5284,6 +5259,7 @@ fn genInlineMemcpy(...@@ -5284,6 +5259,7 @@ fn genInlineMemcpy(
5284 try self.performReloc(loop_reloc);5259 try self.performReloc(loop_reloc);
5285}5260}
52865261
5262/// Spills .rax register.
5287fn genInlineMemset(5263fn genInlineMemset(
5288 self: *Self,5264 self: *Self,
5289 dst_ptr: MCValue,5265 dst_ptr: MCValue,
...@@ -5291,9 +5267,7 @@ fn genInlineMemset(...@@ -5291,9 +5267,7 @@ fn genInlineMemset(
5291 len: MCValue,5267 len: MCValue,
5292 opts: InlineMemcpyOpts,5268 opts: InlineMemcpyOpts,
5293) InnerError!void {5269) InnerError!void {
5294 try self.register_manager.getReg(.rax, null);
5295 self.register_manager.freezeRegs(&.{.rax});5270 self.register_manager.freezeRegs(&.{.rax});
5296 defer self.register_manager.unfreezeRegs(&.{.rax});
52975271
5298 const addr_reg = try self.register_manager.allocReg(null);5272 const addr_reg = try self.register_manager.allocReg(null);
5299 switch (dst_ptr) {5273 switch (dst_ptr) {
...@@ -5330,6 +5304,9 @@ fn genInlineMemset(...@@ -5330,6 +5304,9 @@ fn genInlineMemset(
5330 self.register_manager.freezeRegs(&.{addr_reg});5304 self.register_manager.freezeRegs(&.{addr_reg});
5331 defer self.register_manager.unfreezeRegs(&.{addr_reg});5305 defer self.register_manager.unfreezeRegs(&.{addr_reg});
53325306
5307 self.register_manager.unfreezeRegs(&.{.rax});
5308 try self.register_manager.getReg(.rax, null);
5309
5333 try self.genSetReg(Type.usize, .rax, len);5310 try self.genSetReg(Type.usize, .rax, len);
5334 try self.genBinMathOpMir(.sub, Type.usize, .{ .register = .rax }, .{ .immediate = 1 });5311 try self.genBinMathOpMir(.sub, Type.usize, .{ .register = .rax }, .{ .immediate = 1 });
53355312