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
17371737 .immediate => |imm| {
17381738 try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm });
17391739 },
1740 .stack_offset => {
1741 const reg = try self.copyToTmpRegister(ptr_ty, ptr);
1742 try self.load(dst_mcv, .{ .register = reg }, ptr_ty);
1743 },
17401744 .ptr_stack_offset => |off| {
17411745 try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off });
17421746 },
......@@ -1787,9 +1791,6 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
17871791 const reg = try self.copyToTmpRegister(ptr_ty, ptr);
17881792 try self.load(dst_mcv, .{ .register = reg }, ptr_ty);
17891793 },
1790 .stack_offset => {
1791 return self.fail("TODO implement loading from MCValue.stack_offset", .{});
1792 },
17931794 }
17941795}
17951796
......@@ -1832,6 +1833,10 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
18321833 .immediate => |imm| {
18331834 try self.setRegOrMem(value_ty, .{ .memory = imm }, value);
18341835 },
1836 .stack_offset => {
1837 const reg = try self.copyToTmpRegister(ptr_ty, ptr);
1838 try self.store(.{ .register = reg }, value, ptr_ty, value_ty);
1839 },
18351840 .ptr_stack_offset => |off| {
18361841 try self.genSetStack(value_ty, off, value);
18371842 },
......@@ -1909,6 +1914,10 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
19091914 .data = .{ .imm = 0 },
19101915 });
19111916 },
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 },
19121921 else => |other| {
19131922 return self.fail("TODO implement set pointee with {}", .{other});
19141923 },
......@@ -2020,9 +2029,6 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
20202029 else => return self.fail("TODO implement storing {} to MCValue.memory", .{value}),
20212030 }
20222031 },
2023 .stack_offset => {
2024 return self.fail("TODO implement storing to MCValue.stack_offset", .{});
2025 },
20262032 }
20272033}
20282034
......@@ -2452,7 +2458,18 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !
24522458 return self.genIMulOpMir(dst_ty, dst_mcv, MCValue{ .register = src_reg });
24532459 }
24542460 },
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 => {
24562473 return self.fail("TODO implement x86 multiply source memory", .{});
24572474 },
24582475 .got_load, .direct_load => {
......@@ -3520,6 +3537,13 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
35203537 .dead => unreachable,
35213538 .ptr_embedded_in_code => unreachable,
35223539 .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 },
35233547 .compare_flags_unsigned,
35243548 .compare_flags_signed,
35253549 => {
......@@ -3598,7 +3622,6 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
35983622
35993623 try self.genInlineMemcpy(stack_offset, .rsp, ty, mcv);
36003624 },
3601 else => return self.fail("TODO implement args on stack for {}", .{mcv}),
36023625 }
36033626}
36043627
......@@ -3617,7 +3640,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerErro
36173640 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }),
36183641 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),
36193642 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 }),
36213644 }
36223645 },
36233646 .compare_flags_unsigned,
......@@ -3943,12 +3966,20 @@ fn genInlineMemcpy(self: *Self, stack_offset: i32, stack_reg: Register, ty: Type
39433966 try self.performReloc(loop_reloc);
39443967}
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 {
39473976 try self.register_manager.getReg(.rax, null);
3977
39483978 const abi_size = ty.abiSize(self.target.*);
39493979 if (stack_offset > 128) {
39503980 return self.fail("TODO inline memset with large stack offset", .{});
39513981 }
3982
39523983 const negative_offset = @bitCast(u32, -stack_offset);
39533984
39543985 // 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
40054036 _ = try self.addInst(.{
40064037 .tag = .mov_mem_index_imm,
40074038 .ops = (Mir.Ops{
4008 .reg1 = .rbp,
4039 .reg1 = stack_register.to64(),
40094040 }).encode(),
40104041 .data = .{ .payload = payload },
40114042 });
......@@ -4731,20 +4762,40 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
47314762 var next_int_reg: usize = 0;
47324763 var by_reg = std.AutoHashMap(usize, usize).init(self.bin_file.allocator);
47334764 defer by_reg.deinit();
4734 for (param_types) |ty, i| {
4735 if (!ty.hasRuntimeBits()) continue;
4736 const param_size = @intCast(u32, ty.abiSize(self.target.*));
4737 const pass_in_reg = switch (ty.zigTypeTag()) {
4738 .Bool => true,
4739 .Int, .Enum => param_size <= 8,
4740 .Pointer => ty.ptrSize() != .Slice,
4741 .Optional => ty.isPtrLikeOptional(),
4742 else => false,
4743 };
4744 if (pass_in_reg) {
4745 if (next_int_reg >= c_abi_int_param_regs.len) break;
4746 try by_reg.putNoClobber(i, next_int_reg);
4747 next_int_reg += 1;
4765
4766 // If we want debug output, we store all args on stack for better liveness of args
4767 // in debugging contexts such as previewing the args in the debugger anywhere in
4768 // the procedure. Passing the args via registers can lead to reusing the register
4769 // for local ops thus clobbering the input arg forever.
4770 // This of course excludes C ABI calls.
4771 const omit_args_in_registers = blk: {
4772 if (cc == .C) break :blk false;
4773 switch (self.bin_file.options.optimize_mode) {
4774 .Debug => break :blk true,
4775 else => break :blk false,
4776 }
4777 };
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 }
47484799 }
47494800 }
47504801
......@@ -4765,11 +4816,6 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
47654816 result.args[i] = .{ .register = aliased_reg };
47664817 next_int_reg += 1;
47674818 } 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.
47734819 const offset = mem.alignForwardGeneric(u32, next_stack_offset + param_size, param_align);
47744820 result.args[i] = .{ .stack_offset = @intCast(i32, offset) };
47754821 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 {
691691 0b00 => {
692692 return lowerToRmEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
693693 },
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 },
694702 0b10 => {
695703 const imm = emit.mir.instructions.items(.data)[inst].imm;
696704 return lowerToRmiEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), imm, emit.code);
697705 },
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 },
699714 }
700715}
701716