| ... | ... | @@ -1737,6 +1737,10 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 1737 | 1737 | .immediate => |imm| { |
| 1738 | 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 | 1744 | .ptr_stack_offset => |off| { |
| 1741 | 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 | 1791 | const reg = try self.copyToTmpRegister(ptr_ty, ptr); |
| 1788 | 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 | 1833 | .immediate => |imm| { |
| 1833 | 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 | 1840 | .ptr_stack_offset => |off| { |
| 1836 | 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 | 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 | 1921 | else => |other| { |
| 1913 | 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 | 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 | 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 | 2473 | return self.fail("TODO implement x86 multiply source memory", .{}); |
| 2457 | 2474 | }, |
| 2458 | 2475 | .got_load, .direct_load => { |
| ... | ... | @@ -3520,6 +3537,13 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE |
| 3520 | 3537 | .dead => unreachable, |
| 3521 | 3538 | .ptr_embedded_in_code => unreachable, |
| 3522 | 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 | 3547 | .compare_flags_unsigned, |
| 3524 | 3548 | .compare_flags_signed, |
| 3525 | 3549 | => { |
| ... | ... | @@ -3598,7 +3622,6 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE |
| 3598 | 3622 | |
| 3599 | 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 | 3640 | 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }), |
| 3618 | 3641 | 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }), |
| 3619 | 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 | 3646 | .compare_flags_unsigned, |
| ... | ... | @@ -3943,12 +3966,20 @@ fn genInlineMemcpy(self: *Self, stack_offset: i32, stack_reg: Register, ty: Type |
| 3943 | 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 | 3976 | try self.register_manager.getReg(.rax, null); |
| 3977 | |
| 3948 | 3978 | const abi_size = ty.abiSize(self.target.*); |
| 3949 | 3979 | if (stack_offset > 128) { |
| 3950 | 3980 | return self.fail("TODO inline memset with large stack offset", .{}); |
| 3951 | 3981 | } |
| 3982 | |
| 3952 | 3983 | const negative_offset = @bitCast(u32, -stack_offset); |
| 3953 | 3984 | |
| 3954 | 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 | 4036 | _ = try self.addInst(.{ |
| 4006 | 4037 | .tag = .mov_mem_index_imm, |
| 4007 | 4038 | .ops = (Mir.Ops{ |
| 4008 | | .reg1 = .rbp, |
| 4039 | .reg1 = stack_register.to64(), |
| 4009 | 4040 | }).encode(), |
| 4010 | 4041 | .data = .{ .payload = payload }, |
| 4011 | 4042 | }); |
| ... | ... | @@ -4731,20 +4762,40 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 4731 | 4762 | var next_int_reg: usize = 0; |
| 4732 | 4763 | var by_reg = std.AutoHashMap(usize, usize).init(self.bin_file.allocator); |
| 4733 | 4764 | 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 | } |
| 4748 | 4799 | } |
| 4749 | 4800 | } |
| 4750 | 4801 | |
| ... | ... | @@ -4765,11 +4816,6 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 4765 | 4816 | result.args[i] = .{ .register = aliased_reg }; |
| 4766 | 4817 | next_int_reg += 1; |
| 4767 | 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 | 4819 | const offset = mem.alignForwardGeneric(u32, next_stack_offset + param_size, param_align); |
| 4774 | 4820 | result.args[i] = .{ .stack_offset = @intCast(i32, offset) }; |
| 4775 | 4821 | next_stack_offset = offset; |