| ... | @@ -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 | } |
| 1795 | | 1796 | |
| ... | @@ -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 | } |
| 2028 | | 2034 | |
| ... | @@ -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 |
| 3598 | | 3622 | |
| 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 | } |
| 3604 | | 3627 | |
| ... | @@ -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 | } |
| 3945 | | 3968 | |
| 3946 | fn genInlineMemset(self: *Self, ty: Type, stack_offset: i32, value: MCValue) InnerError!void { | 3969 | fn 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); |
| 3953 | | 3984 | |
| 3954 | // We are actually counting `abi_size` bytes; however, we reuse the index register | 3985 | // 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 | } |
| 4750 | | 4801 | |
| ... | @@ -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; |