| ... | @@ -58,7 +58,6 @@ arg_index: u32, | ... | @@ -58,7 +58,6 @@ arg_index: u32, |
| 58 | src_loc: Module.SrcLoc, | 58 | src_loc: Module.SrcLoc, |
| 59 | stack_align: u32, | 59 | stack_align: u32, |
| 60 | | 60 | |
| 61 | ret_backpatches: std.ArrayListUnmanaged(Mir.Inst.Index) = .{}, | | |
| 62 | compare_flags_inst: ?Air.Inst.Index = null, | 61 | compare_flags_inst: ?Air.Inst.Index = null, |
| 63 | | 62 | |
| 64 | /// MIR Instructions | 63 | /// 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 | }); |
| 484 | | 482 | |
| | 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 appending | 908 | // 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 | } |
| 3317 | | 3312 | |
| 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. |
| 3321 | fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void { | 3315 | fn 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}); |
| 3533 | | 3528 | |
| 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}); |
| 3537 | | 3532 | |
| 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. |
| 3720 | | 3715 | |
| 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 register | 3720 | 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 | }; |
| 5094 | | 5071 | |
| | 5072 | /// Spills .rax and .rcx. |
| 5095 | fn genInlineMemcpy( | 5073 | fn 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 }); | | |
| 5109 | | 5081 | |
| 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}); |
| 5151 | | 5122 | |
| ... | @@ -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}); |
| 5187 | | 5157 | |
| ... | @@ -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(); |
| 5191 | | 5161 | |
| | 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); |
| 5193 | | 5168 | |
| 5194 | // mov rcx, 0 | 5169 | // 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 | } |
| 5286 | | 5261 | |
| | 5262 | /// Spills .rax register. |
| 5287 | fn genInlineMemset( | 5263 | fn 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}); | | |
| 5297 | | 5271 | |
| 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}); |
| 5332 | | 5306 | |
| | 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 }); |
| 5335 | | 5312 | |