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,
5858src_loc: Module.SrcLoc,
5959stack_align: u32,
6060
61ret_backpatches: std.ArrayListUnmanaged(Mir.Inst.Index) = .{},
6261compare_flags_inst: ?Air.Inst.Index = null,
6362
6463/// MIR Instructions
......@@ -353,7 +352,6 @@ pub fn generate(
353352 std.AutoHashMap(Mir.Inst.Index, Air.Inst.Index).init(bin_file.allocator)
354353 else {},
355354 };
356 defer function.ret_backpatches.deinit(bin_file.allocator);
357355 defer function.stack.deinit(bin_file.allocator);
358356 defer function.blocks.deinit(bin_file.allocator);
359357 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
......@@ -482,6 +480,21 @@ fn gen(self: *Self) InnerError!void {
482480 .data = undefined,
483481 });
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
485498 _ = try self.addInst(.{
486499 .tag = .dbg_prologue_end,
487500 .ops = undefined,
......@@ -519,23 +532,6 @@ fn gen(self: *Self) InnerError!void {
519532 var disp = data.disp + 8;
520533 inline for (callee_preserved_regs) |reg, i| {
521534 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 }
539535 data.regs |= 1 << @intCast(u5, i);
540536 self.max_end_stack += 8;
541537 disp += 8;
......@@ -912,8 +908,7 @@ fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u
912908 // TODO find a free slot instead of always appending
913909 const offset = mem.alignForwardGeneric(u32, self.next_stack_offset + abi_size, abi_align);
914910 self.next_stack_offset = offset;
915 if (self.next_stack_offset > self.max_end_stack)
916 self.max_end_stack = self.next_stack_offset;
911 self.max_end_stack = @maximum(self.max_end_stack, self.next_stack_offset);
917912 try self.stack.putNoClobber(self.gpa, offset, .{
918913 .inst = inst,
919914 .size = abi_size,
......@@ -3316,7 +3311,6 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC
33163311}
33173312
33183313/// Performs multi-operand integer multiplication between dst_mcv and src_mcv, storing the result in dst_mcv.
3319/// Does not use/spill .rax/.rdx.
33203314/// Does not support byte-size operands.
33213315fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void {
33223316 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.
35303524 const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*));
35313525 const ret_abi_align = @intCast(u32, ret_ty.abiAlignment(self.target.*));
35323526 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
35343529 try self.register_manager.getReg(.rdi, null);
3535 self.register_manager.freezeRegs(&.{.rdi});
35363530 try self.genSetReg(Type.usize, .rdi, .{ .ptr_stack_offset = stack_offset });
3531 self.register_manager.freezeRegs(&.{.rdi});
35373532
35383533 info.return_value.stack_offset = stack_offset;
35393534 }
......@@ -3720,15 +3715,13 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
37203715
37213716 const result: MCValue = result: {
37223717 switch (info.return_value) {
3723 .register => |reg| {
3724 if (RegisterManager.indexOfRegIntoTracked(reg) == null) {
3725 // Save function return value in a callee saved register
3726 break :result try self.copyToRegisterWithInstTracking(
3727 inst,
3728 self.air.typeOfIndex(inst),
3729 info.return_value,
3730 );
3731 }
3718 .register => {
3719 // Save function return value in a new register
3720 break :result try self.copyToRegisterWithInstTracking(
3721 inst,
3722 self.air.typeOfIndex(inst),
3723 info.return_value,
3724 );
37323725 },
37333726 else => {},
37343727 }
......@@ -3755,19 +3748,11 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void {
37553748 const ret_ty = self.fn_type.fnReturnType();
37563749 switch (self.ret_mcv) {
37573750 .stack_offset => {
3758 // TODO audit register allocation!
3759 self.register_manager.freezeRegs(&.{ .rax, .rcx, .rdi });
3760 defer self.register_manager.unfreezeRegs(&.{ .rax, .rcx, .rdi });
3761 const reg = try self.register_manager.allocReg(null);
3762 const backpatch = try self.addInst(.{
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);
3751 self.register_manager.freezeRegs(&.{ .rax, .rcx });
3752 defer self.register_manager.unfreezeRegs(&.{ .rax, .rcx });
3753 const reg = try self.copyToTmpRegister(Type.usize, self.ret_mcv);
3754 self.register_manager.freezeRegs(&.{reg});
3755 defer self.register_manager.unfreezeRegs(&.{reg});
37713756 try self.genSetStack(ret_ty, 0, operand, .{
37723757 .source_stack_base = .rbp,
37733758 .dest_stack_base = reg,
......@@ -3798,19 +3783,11 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
37983783 const elem_ty = ptr_ty.elemType();
37993784 switch (self.ret_mcv) {
38003785 .stack_offset => {
3801 // TODO audit register allocation!
3802 self.register_manager.freezeRegs(&.{ .rax, .rcx, .rdi });
3803 defer self.register_manager.unfreezeRegs(&.{ .rax, .rcx, .rdi });
3804 const reg = try self.register_manager.allocReg(null);
3805 const backpatch = try self.addInst(.{
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);
3786 self.register_manager.freezeRegs(&.{ .rax, .rcx });
3787 defer self.register_manager.unfreezeRegs(&.{ .rax, .rcx });
3788 const reg = try self.copyToTmpRegister(Type.usize, self.ret_mcv);
3789 self.register_manager.freezeRegs(&.{reg});
3790 defer self.register_manager.unfreezeRegs(&.{reg});
38143791 try self.genInlineMemcpy(.{ .stack_offset = 0 }, ptr, .{ .immediate = elem_ty.abiSize(self.target.*) }, .{
38153792 .source_stack_base = .rbp,
38163793 .dest_stack_base = reg,
......@@ -5092,6 +5069,7 @@ const InlineMemcpyOpts = struct {
50925069 dest_stack_base: ?Register = null,
50935070};
50945071
5072/// Spills .rax and .rcx.
50955073fn genInlineMemcpy(
50965074 self: *Self,
50975075 dst_ptr: MCValue,
......@@ -5099,13 +5077,7 @@ fn genInlineMemcpy(
50995077 len: MCValue,
51005078 opts: InlineMemcpyOpts,
51015079) 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
51075080 self.register_manager.freezeRegs(&.{ .rax, .rcx });
5108 defer self.register_manager.unfreezeRegs(&.{ .rax, .rcx });
51095081
51105082 if (opts.source_stack_base) |reg| self.register_manager.freezeRegs(&.{reg});
51115083 defer if (opts.source_stack_base) |reg| self.register_manager.unfreezeRegs(&.{reg});
......@@ -5145,7 +5117,6 @@ fn genInlineMemcpy(
51455117 return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr});
51465118 },
51475119 }
5148
51495120 self.register_manager.freezeRegs(&.{dst_addr_reg});
51505121 defer self.register_manager.unfreezeRegs(&.{dst_addr_reg});
51515122
......@@ -5181,7 +5152,6 @@ fn genInlineMemcpy(
51815152 return self.fail("TODO implement memcpy for setting stack when src is {}", .{src_ptr});
51825153 },
51835154 }
5184
51855155 self.register_manager.freezeRegs(&.{src_addr_reg});
51865156 defer self.register_manager.unfreezeRegs(&.{src_addr_reg});
51875157
......@@ -5189,6 +5159,11 @@ fn genInlineMemcpy(
51895159 const count_reg = regs[0].to64();
51905160 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
51925167 try self.genSetReg(Type.usize, count_reg, len);
51935168
51945169 // mov rcx, 0
......@@ -5284,6 +5259,7 @@ fn genInlineMemcpy(
52845259 try self.performReloc(loop_reloc);
52855260}
52865261
5262/// Spills .rax register.
52875263fn genInlineMemset(
52885264 self: *Self,
52895265 dst_ptr: MCValue,
......@@ -5291,9 +5267,7 @@ fn genInlineMemset(
52915267 len: MCValue,
52925268 opts: InlineMemcpyOpts,
52935269) InnerError!void {
5294 try self.register_manager.getReg(.rax, null);
52955270 self.register_manager.freezeRegs(&.{.rax});
5296 defer self.register_manager.unfreezeRegs(&.{.rax});
52975271
52985272 const addr_reg = try self.register_manager.allocReg(null);
52995273 switch (dst_ptr) {
......@@ -5330,6 +5304,9 @@ fn genInlineMemset(
53305304 self.register_manager.freezeRegs(&.{addr_reg});
53315305 defer self.register_manager.unfreezeRegs(&.{addr_reg});
53325306
5307 self.register_manager.unfreezeRegs(&.{.rax});
5308 try self.register_manager.getReg(.rax, null);
5309
53335310 try self.genSetReg(Type.usize, .rax, len);
53345311 try self.genBinMathOpMir(.sub, Type.usize, .{ .register = .rax }, .{ .immediate = 1 });
53355312