authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-02-17 16:38:05+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-02-18 09:14:15+01:00
log6d3c7bd4362b8f2415bc1b0d7fee6b30668bdfcd
tree73b20e2c34ce4e7740eb19f3f987e4ab082364ba
parent56e9575e827208b3df5c90472826f52bfc8342c0

x64: pass all args on stack in debug and if not extern fn

If the function call is not extern and we are compiling in debug, pass function params always on stack. This will improve debugging capabilities since the params will not be volatile and possibly clobbered by the procedure code. Finish implementation of `imul_complex`.

2 files changed, 92 insertions(+), 31 deletions(-)

src/arch/x86_64/CodeGen.zig+76-30
...@@ -1737,6 +1737,10 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo...@@ -1737,6 +1737,10 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
1737 .immediate => |imm| {1737 .immediate => |imm| {
1738 try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm });1738 try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm });
1739 },1739 },
1740 .stack_offset => {
1741 const reg = try self.copyToTmpRegister(ptr_ty, ptr);
1742 try self.load(dst_mcv, .{ .register = reg }, ptr_ty);
1743 },
1740 .ptr_stack_offset => |off| {1744 .ptr_stack_offset => |off| {
1741 try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off });1745 try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off });
1742 },1746 },
...@@ -1787,9 +1791,6 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo...@@ -1787,9 +1791,6 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
1787 const reg = try self.copyToTmpRegister(ptr_ty, ptr);1791 const reg = try self.copyToTmpRegister(ptr_ty, ptr);
1788 try self.load(dst_mcv, .{ .register = reg }, ptr_ty);1792 try self.load(dst_mcv, .{ .register = reg }, ptr_ty);
1789 },1793 },
1790 .stack_offset => {
1791 return self.fail("TODO implement loading from MCValue.stack_offset", .{});
1792 },
1793 }1794 }
1794}1795}
17951796
...@@ -1832,6 +1833,10 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -1832,6 +1833,10 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
1832 .immediate => |imm| {1833 .immediate => |imm| {
1833 try self.setRegOrMem(value_ty, .{ .memory = imm }, value);1834 try self.setRegOrMem(value_ty, .{ .memory = imm }, value);
1834 },1835 },
1836 .stack_offset => {
1837 const reg = try self.copyToTmpRegister(ptr_ty, ptr);
1838 try self.store(.{ .register = reg }, value, ptr_ty, value_ty);
1839 },
1835 .ptr_stack_offset => |off| {1840 .ptr_stack_offset => |off| {
1836 try self.genSetStack(value_ty, off, value);1841 try self.genSetStack(value_ty, off, value);
1837 },1842 },
...@@ -1909,6 +1914,10 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -1909,6 +1914,10 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
1909 .data = .{ .imm = 0 },1914 .data = .{ .imm = 0 },
1910 });1915 });
1911 },1916 },
1917 .stack_offset => {
1918 const tmp_reg = try self.copyToTmpRegister(value_ty, value);
1919 return self.store(ptr, .{ .register = tmp_reg }, ptr_ty, value_ty);
1920 },
1912 else => |other| {1921 else => |other| {
1913 return self.fail("TODO implement set pointee with {}", .{other});1922 return self.fail("TODO implement set pointee with {}", .{other});
1914 },1923 },
...@@ -2020,9 +2029,6 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -2020,9 +2029,6 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
2020 else => return self.fail("TODO implement storing {} to MCValue.memory", .{value}),2029 else => return self.fail("TODO implement storing {} to MCValue.memory", .{value}),
2021 }2030 }
2022 },2031 },
2023 .stack_offset => {
2024 return self.fail("TODO implement storing to MCValue.stack_offset", .{});
2025 },
2026 }2032 }
2027}2033}
20282034
...@@ -2452,7 +2458,18 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !...@@ -2452,7 +2458,18 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !
2452 return self.genIMulOpMir(dst_ty, dst_mcv, MCValue{ .register = src_reg });2458 return self.genIMulOpMir(dst_ty, dst_mcv, MCValue{ .register = src_reg });
2453 }2459 }
2454 },2460 },
2455 .embedded_in_code, .memory, .stack_offset => {2461 .stack_offset => |off| {
2462 _ = try self.addInst(.{
2463 .tag = .imul_complex,
2464 .ops = (Mir.Ops{
2465 .reg1 = dst_reg,
2466 .reg2 = .rbp,
2467 .flags = 0b01,
2468 }).encode(),
2469 .data = .{ .imm = @bitCast(u32, -off) },
2470 });
2471 },
2472 .embedded_in_code, .memory => {
2456 return self.fail("TODO implement x86 multiply source memory", .{});2473 return self.fail("TODO implement x86 multiply source memory", .{});
2457 },2474 },
2458 .got_load, .direct_load => {2475 .got_load, .direct_load => {
...@@ -3520,6 +3537,13 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE...@@ -3520,6 +3537,13 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
3520 .dead => unreachable,3537 .dead => unreachable,
3521 .ptr_embedded_in_code => unreachable,3538 .ptr_embedded_in_code => unreachable,
3522 .unreach, .none => return,3539 .unreach, .none => return,
3540 .undef => {
3541 if (abi_size <= 8) {
3542 const reg = try self.copyToTmpRegister(ty, mcv);
3543 return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg });
3544 }
3545 try self.genInlineMemset(stack_offset, .rsp, ty, .{ .immediate = 0xaa });
3546 },
3523 .compare_flags_unsigned,3547 .compare_flags_unsigned,
3524 .compare_flags_signed,3548 .compare_flags_signed,
3525 => {3549 => {
...@@ -3598,7 +3622,6 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE...@@ -3598,7 +3622,6 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
35983622
3599 try self.genInlineMemcpy(stack_offset, .rsp, ty, mcv);3623 try self.genInlineMemcpy(stack_offset, .rsp, ty, mcv);
3600 },3624 },
3601 else => return self.fail("TODO implement args on stack for {}", .{mcv}),
3602 }3625 }
3603}3626}
36043627
...@@ -3617,7 +3640,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerErro...@@ -3617,7 +3640,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerErro
3617 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }),3640 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }),
3618 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),3641 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),
3619 8 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),3642 8 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
3620 else => return self.genInlineMemset(ty, stack_offset, .{ .immediate = 0xaa }),3643 else => return self.genInlineMemset(stack_offset, .rbp, ty, .{ .immediate = 0xaa }),
3621 }3644 }
3622 },3645 },
3623 .compare_flags_unsigned,3646 .compare_flags_unsigned,
...@@ -3943,12 +3966,20 @@ fn genInlineMemcpy(self: *Self, stack_offset: i32, stack_reg: Register, ty: Type...@@ -3943,12 +3966,20 @@ fn genInlineMemcpy(self: *Self, stack_offset: i32, stack_reg: Register, ty: Type
3943 try self.performReloc(loop_reloc);3966 try self.performReloc(loop_reloc);
3944}3967}
39453968
3946fn genInlineMemset(self: *Self, ty: Type, stack_offset: i32, value: MCValue) InnerError!void {3969fn genInlineMemset(
3970 self: *Self,
3971 stack_offset: i32,
3972 stack_register: Register,
3973 ty: Type,
3974 value: MCValue,
3975) InnerError!void {
3947 try self.register_manager.getReg(.rax, null);3976 try self.register_manager.getReg(.rax, null);
3977
3948 const abi_size = ty.abiSize(self.target.*);3978 const abi_size = ty.abiSize(self.target.*);
3949 if (stack_offset > 128) {3979 if (stack_offset > 128) {
3950 return self.fail("TODO inline memset with large stack offset", .{});3980 return self.fail("TODO inline memset with large stack offset", .{});
3951 }3981 }
3982
3952 const negative_offset = @bitCast(u32, -stack_offset);3983 const negative_offset = @bitCast(u32, -stack_offset);
39533984
3954 // We are actually counting `abi_size` bytes; however, we reuse the index register3985 // We are actually counting `abi_size` bytes; however, we reuse the index register
...@@ -4005,7 +4036,7 @@ fn genInlineMemset(self: *Self, ty: Type, stack_offset: i32, value: MCValue) Inn...@@ -4005,7 +4036,7 @@ fn genInlineMemset(self: *Self, ty: Type, stack_offset: i32, value: MCValue) Inn
4005 _ = try self.addInst(.{4036 _ = try self.addInst(.{
4006 .tag = .mov_mem_index_imm,4037 .tag = .mov_mem_index_imm,
4007 .ops = (Mir.Ops{4038 .ops = (Mir.Ops{
4008 .reg1 = .rbp,4039 .reg1 = stack_register.to64(),
4009 }).encode(),4040 }).encode(),
4010 .data = .{ .payload = payload },4041 .data = .{ .payload = payload },
4011 });4042 });
...@@ -4731,20 +4762,40 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {...@@ -4731,20 +4762,40 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
4731 var next_int_reg: usize = 0;4762 var next_int_reg: usize = 0;
4732 var by_reg = std.AutoHashMap(usize, usize).init(self.bin_file.allocator);4763 var by_reg = std.AutoHashMap(usize, usize).init(self.bin_file.allocator);
4733 defer by_reg.deinit();4764 defer by_reg.deinit();
4734 for (param_types) |ty, i| {4765
4735 if (!ty.hasRuntimeBits()) continue;4766 // If we want debug output, we store all args on stack for better liveness of args
4736 const param_size = @intCast(u32, ty.abiSize(self.target.*));4767 // in debugging contexts such as previewing the args in the debugger anywhere in
4737 const pass_in_reg = switch (ty.zigTypeTag()) {4768 // the procedure. Passing the args via registers can lead to reusing the register
4738 .Bool => true,4769 // for local ops thus clobbering the input arg forever.
4739 .Int, .Enum => param_size <= 8,4770 // This of course excludes C ABI calls.
4740 .Pointer => ty.ptrSize() != .Slice,4771 const omit_args_in_registers = blk: {
4741 .Optional => ty.isPtrLikeOptional(),4772 if (cc == .C) break :blk false;
4742 else => false,4773 switch (self.bin_file.options.optimize_mode) {
4743 };4774 .Debug => break :blk true,
4744 if (pass_in_reg) {4775 else => break :blk false,
4745 if (next_int_reg >= c_abi_int_param_regs.len) break;4776 }
4746 try by_reg.putNoClobber(i, next_int_reg);4777 };
4747 next_int_reg += 1;4778 if (!omit_args_in_registers) {
4779 for (param_types) |ty, i| {
4780 if (!ty.hasRuntimeBits()) continue;
4781 const param_size = @intCast(u32, ty.abiSize(self.target.*));
4782 // For simplicity of codegen, slices and other types are always pushed onto the stack.
4783 // TODO: look into optimizing this by passing things as registers sometimes,
4784 // such as ptr and len of slices as separate registers.
4785 // TODO: also we need to honor the C ABI for relevant types rather than passing on
4786 // the stack here.
4787 const pass_in_reg = switch (ty.zigTypeTag()) {
4788 .Bool => true,
4789 .Int, .Enum => param_size <= 8,
4790 .Pointer => ty.ptrSize() != .Slice,
4791 .Optional => ty.isPtrLikeOptional(),
4792 else => false,
4793 };
4794 if (pass_in_reg) {
4795 if (next_int_reg >= c_abi_int_param_regs.len) break;
4796 try by_reg.putNoClobber(i, next_int_reg);
4797 next_int_reg += 1;
4798 }
4748 }4799 }
4749 }4800 }
47504801
...@@ -4765,11 +4816,6 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {...@@ -4765,11 +4816,6 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
4765 result.args[i] = .{ .register = aliased_reg };4816 result.args[i] = .{ .register = aliased_reg };
4766 next_int_reg += 1;4817 next_int_reg += 1;
4767 } else {4818 } else {
4768 // For simplicity of codegen, slices and other types are always pushed onto the stack.
4769 // TODO: look into optimizing this by passing things as registers sometimes,
4770 // such as ptr and len of slices as separate registers.
4771 // TODO: also we need to honor the C ABI for relevant types rather than passing on
4772 // the stack here.
4773 const offset = mem.alignForwardGeneric(u32, next_stack_offset + param_size, param_align);4819 const offset = mem.alignForwardGeneric(u32, next_stack_offset + param_size, param_align);
4774 result.args[i] = .{ .stack_offset = @intCast(i32, offset) };4820 result.args[i] = .{ .stack_offset = @intCast(i32, offset) };
4775 next_stack_offset = offset;4821 next_stack_offset = offset;
src/arch/x86_64/Emit.zig+16-1
...@@ -691,11 +691,26 @@ fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -691,11 +691,26 @@ fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
691 0b00 => {691 0b00 => {
692 return lowerToRmEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);692 return lowerToRmEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
693 },693 },
694 0b01 => {
695 const imm = emit.mir.instructions.items(.data)[inst].imm;
696 const src_reg: ?Register = if (ops.reg2 == .none) null else ops.reg2;
697 return lowerToRmEnc(.imul, ops.reg1, RegisterOrMemory.mem(.qword_ptr, .{
698 .disp = imm,
699 .base = src_reg,
700 }), emit.code);
701 },
694 0b10 => {702 0b10 => {
695 const imm = emit.mir.instructions.items(.data)[inst].imm;703 const imm = emit.mir.instructions.items(.data)[inst].imm;
696 return lowerToRmiEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), imm, emit.code);704 return lowerToRmiEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), imm, emit.code);
697 },705 },
698 else => return emit.fail("TODO implement imul", .{}),706 0b11 => {
707 const payload = emit.mir.instructions.items(.data)[inst].payload;
708 const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data;
709 return lowerToRmiEnc(.imul, ops.reg1, RegisterOrMemory.mem(.qword_ptr, .{
710 .disp = imm_pair.dest_off,
711 .base = ops.reg2,
712 }), imm_pair.operand, emit.code);
713 },
699 }714 }
700}715}
701716