authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-04-02 21:39:20-04:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-04-03 17:45:16+02:00
log272acb7ee5368a32d9f4bc559e93d96759b80173
tree77875a38a3f1cfa8c6f062bb9472f7314c82f9f2
parentf5f0d95f0e882ce43146b269f994c6c242f9ed3b

x86_64: implement storing large immediates


3 files changed, 98 insertions(+), 147 deletions(-)

src/arch/x86_64/CodeGen.zig+98-143
...@@ -3669,52 +3669,67 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -3669,52 +3669,67 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
3669 .none => unreachable,3669 .none => unreachable,
3670 .dead => unreachable,3670 .dead => unreachable,
3671 .unreach => unreachable,3671 .unreach => unreachable,
3672 .eflags => |cc| {3672 .eflags => |cc| try self.asmSetccMemory(
3673 try self.asmSetccMemory(Memory.sib(3673 Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = reg.to64() }),
3674 Memory.PtrSize.fromSize(abi_size),3674 cc,
3675 .{ .base = reg.to64() },3675 ),
3676 ), cc);3676 .undef => if (self.wantSafety()) switch (abi_size) {
3677 },3677 1 => try self.store(ptr, .{ .immediate = 0xaa }, ptr_ty, value_ty),
3678 .undef => {3678 2 => try self.store(ptr, .{ .immediate = 0xaaaa }, ptr_ty, value_ty),
3679 if (!self.wantSafety()) return; // The already existing value will do just fine.3679 4 => try self.store(ptr, .{ .immediate = 0xaaaaaaaa }, ptr_ty, value_ty),
3680 switch (abi_size) {3680 8 => try self.store(ptr, .{ .immediate = 0xaaaaaaaaaaaaaaaa }, ptr_ty, value_ty),
3681 1 => try self.store(ptr, .{ .immediate = 0xaa }, ptr_ty, value_ty),3681 else => try self.genInlineMemset(
3682 2 => try self.store(ptr, .{ .immediate = 0xaaaa }, ptr_ty, value_ty),3682 ptr,
3683 4 => try self.store(ptr, .{ .immediate = 0xaaaaaaaa }, ptr_ty, value_ty),3683 .{ .immediate = 0xaa },
3684 8 => try self.store(ptr, .{ .immediate = 0xaaaaaaaaaaaaaaaa }, ptr_ty, value_ty),3684 .{ .immediate = abi_size },
3685 else => try self.genInlineMemset(ptr, .{ .immediate = 0xaa }, .{ .immediate = abi_size }, .{}),3685 .{},
3686 }3686 ),
3687 },
3688 .immediate => |imm| {
3689 switch (abi_size) {
3690 1, 2, 4 => {
3691 const immediate = if (value_ty.isSignedInt())
3692 Immediate.s(@intCast(i32, @bitCast(i64, imm)))
3693 else
3694 Immediate.u(@truncate(u32, imm));
3695 try self.asmMemoryImmediate(.mov, Memory.sib(
3696 Memory.PtrSize.fromSize(abi_size),
3697 .{ .base = reg.to64() },
3698 ), immediate);
3699 },
3700 8 => {
3701 // TODO: optimization: if the imm is only using the lower
3702 // 4 bytes and can be sign extended we can use a normal mov
3703 // with indirect addressing (mov [reg64], imm32).
3704
3705 // movabs does not support indirect register addressing
3706 // so we need an extra register and an extra mov.
3707 const tmp_reg = try self.copyToTmpRegister(value_ty, value);
3708 return self.store(ptr, .{ .register = tmp_reg }, ptr_ty, value_ty);
3709 },
3710 else => {
3711 return self.fail("TODO implement set pointee with immediate of ABI size {d}", .{abi_size});
3712 },
3713 }
3714 },3687 },
3715 .register => |src_reg| {3688 .immediate => |imm| switch (self.regBitSize(value_ty)) {
3716 try self.genInlineMemcpyRegisterRegister(value_ty, reg, src_reg, 0);3689 8 => try self.asmMemoryImmediate(
3690 .mov,
3691 Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = reg.to64() }),
3692 if (math.cast(i8, @bitCast(i64, imm))) |small|
3693 Immediate.s(small)
3694 else
3695 Immediate.u(@intCast(u8, imm)),
3696 ),
3697 16 => try self.asmMemoryImmediate(
3698 .mov,
3699 Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = reg.to64() }),
3700 if (math.cast(i16, @bitCast(i64, imm))) |small|
3701 Immediate.s(small)
3702 else
3703 Immediate.u(@intCast(u16, imm)),
3704 ),
3705 32 => try self.asmMemoryImmediate(
3706 .mov,
3707 Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = reg.to64() }),
3708 if (math.cast(i32, @bitCast(i64, imm))) |small|
3709 Immediate.s(small)
3710 else
3711 Immediate.u(@intCast(u32, imm)),
3712 ),
3713 64 => if (math.cast(i32, @bitCast(i64, imm))) |small|
3714 try self.asmMemoryImmediate(
3715 .mov,
3716 Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = reg.to64() }),
3717 Immediate.s(small),
3718 )
3719 else
3720 try self.asmMemoryRegister(
3721 .mov,
3722 Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = reg.to64() }),
3723 registerAlias(try self.copyToTmpRegister(value_ty, value), abi_size),
3724 ),
3725 else => unreachable,
3717 },3726 },
3727 .register => |src_reg| try self.genInlineMemcpyRegisterRegister(
3728 value_ty,
3729 reg,
3730 src_reg,
3731 0,
3732 ),
3718 .register_overflow => |ro| {3733 .register_overflow => |ro| {
3719 const ro_reg_lock = self.register_manager.lockReg(ro.reg);3734 const ro_reg_lock = self.register_manager.lockReg(ro.reg);
3720 defer if (ro_reg_lock) |lock| self.register_manager.unlockReg(lock);3735 defer if (ro_reg_lock) |lock| self.register_manager.unlockReg(lock);
...@@ -3765,8 +3780,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -3765,8 +3780,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
3765 defer self.register_manager.unlockReg(addr_reg_lock);3780 defer self.register_manager.unlockReg(addr_reg_lock);
37663781
3767 try self.loadMemPtrIntoRegister(addr_reg, ptr_ty, ptr);3782 try self.loadMemPtrIntoRegister(addr_reg, ptr_ty, ptr);
37683783 // Load the pointer, which is stored in memory
3769 // To get the actual address of the value we want to modify we have to go through the GOT
3770 try self.asmRegisterMemory(3784 try self.asmRegisterMemory(
3771 .mov,3785 .mov,
3772 addr_reg.to64(),3786 addr_reg.to64(),
...@@ -3774,62 +3788,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -3774,62 +3788,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
3774 );3788 );
37753789
3776 const new_ptr = MCValue{ .register = addr_reg.to64() };3790 const new_ptr = MCValue{ .register = addr_reg.to64() };
37773791 return self.store(new_ptr, value, ptr_ty, value_ty);
3778 switch (value) {
3779 .immediate => |imm| {
3780 if (abi_size > 8) {
3781 return self.fail("TODO saving imm to memory for abi_size {}", .{abi_size});
3782 }
3783
3784 if (abi_size == 8) {
3785 // TODO
3786 const top_bits: u32 = @intCast(u32, imm >> 32);
3787 const can_extend = if (value_ty.isUnsignedInt())
3788 (top_bits == 0) and (imm & 0x8000_0000) == 0
3789 else
3790 top_bits == 0xffff_ffff;
3791
3792 if (!can_extend) {
3793 return self.fail("TODO imm64 would get incorrectly sign extended", .{});
3794 }
3795 }
3796 try self.asmMemoryImmediate(
3797 .mov,
3798 Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = addr_reg.to64() }),
3799 Immediate.u(@intCast(u32, imm)),
3800 );
3801 },
3802 .register => {
3803 return self.store(new_ptr, value, ptr_ty, value_ty);
3804 },
3805 .linker_load, .memory => {
3806 if (abi_size <= 8) {
3807 const tmp_reg = try self.register_manager.allocReg(null, gp);
3808 const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
3809 defer self.register_manager.unlockReg(tmp_reg_lock);
3810
3811 try self.loadMemPtrIntoRegister(tmp_reg, value_ty, value);
3812 try self.asmRegisterMemory(
3813 .mov,
3814 tmp_reg,
3815 Memory.sib(.qword, .{ .base = tmp_reg }),
3816 );
3817
3818 return self.store(new_ptr, .{ .register = tmp_reg }, ptr_ty, value_ty);
3819 }
3820
3821 try self.genInlineMemcpy(new_ptr, value, .{ .immediate = abi_size }, .{});
3822 },
3823 .stack_offset => {
3824 if (abi_size <= 8) {
3825 const tmp_reg = try self.copyToTmpRegister(value_ty, value);
3826 return self.store(new_ptr, .{ .register = tmp_reg }, ptr_ty, value_ty);
3827 }
3828
3829 try self.genInlineMemcpy(new_ptr, value, .{ .immediate = abi_size }, .{});
3830 },
3831 else => return self.fail("TODO implement storing {} to MCValue.memory", .{value}),
3832 }
3833 },3792 },
3834 }3793 }
3835}3794}
...@@ -4887,41 +4846,39 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, ty: Type, dst_mcv: MCValue, s...@@ -4887,41 +4846,39 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, ty: Type, dst_mcv: MCValue, s
4887 registerAlias(src_reg, abi_size),4846 registerAlias(src_reg, abi_size),
4888 ),4847 ),
4889 },4848 },
4890 .immediate => |imm| {4849 .immediate => |imm| switch (self.regBitSize(ty)) {
4891 switch (self.regBitSize(ty)) {4850 8 => try self.asmRegisterImmediate(
4892 8 => try self.asmRegisterImmediate(4851 mir_tag,
4893 mir_tag,4852 dst_alias,
4894 dst_alias,4853 if (math.cast(i8, @bitCast(i64, imm))) |small|
4895 if (math.cast(i8, @bitCast(i64, imm))) |small|4854 Immediate.s(small)
4896 Immediate.s(small)
4897 else
4898 Immediate.u(@intCast(u8, imm)),
4899 ),
4900 16 => try self.asmRegisterImmediate(
4901 mir_tag,
4902 dst_alias,
4903 if (math.cast(i16, @bitCast(i64, imm))) |small|
4904 Immediate.s(small)
4905 else
4906 Immediate.u(@intCast(u16, imm)),
4907 ),
4908 32 => try self.asmRegisterImmediate(
4909 mir_tag,
4910 dst_alias,
4911 if (math.cast(i32, @bitCast(i64, imm))) |small|
4912 Immediate.s(small)
4913 else
4914 Immediate.u(@intCast(u32, imm)),
4915 ),
4916 64 => if (math.cast(i32, @bitCast(i64, imm))) |small|
4917 try self.asmRegisterImmediate(mir_tag, dst_alias, Immediate.s(small))
4918 else4855 else
4919 try self.asmRegisterRegister(mir_tag, dst_alias, registerAlias(4856 Immediate.u(@intCast(u8, imm)),
4920 try self.copyToTmpRegister(ty, src_mcv),4857 ),
4921 abi_size,4858 16 => try self.asmRegisterImmediate(
4922 )),4859 mir_tag,
4923 else => unreachable,4860 dst_alias,
4924 }4861 if (math.cast(i16, @bitCast(i64, imm))) |small|
4862 Immediate.s(small)
4863 else
4864 Immediate.u(@intCast(u16, imm)),
4865 ),
4866 32 => try self.asmRegisterImmediate(
4867 mir_tag,
4868 dst_alias,
4869 if (math.cast(i32, @bitCast(i64, imm))) |small|
4870 Immediate.s(small)
4871 else
4872 Immediate.u(@intCast(u32, imm)),
4873 ),
4874 64 => if (math.cast(i32, @bitCast(i64, imm))) |small|
4875 try self.asmRegisterImmediate(mir_tag, dst_alias, Immediate.s(small))
4876 else
4877 try self.asmRegisterRegister(mir_tag, dst_alias, registerAlias(
4878 try self.copyToTmpRegister(ty, src_mcv),
4879 abi_size,
4880 )),
4881 else => unreachable,
4925 },4882 },
4926 .memory, .linker_load, .eflags => {4883 .memory, .linker_load, .eflags => {
4927 assert(abi_size <= 8);4884 assert(abi_size <= 8);
...@@ -4931,13 +4888,11 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, ty: Type, dst_mcv: MCValue, s...@@ -4931,13 +4888,11 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, ty: Type, dst_mcv: MCValue, s
4931 const reg = try self.copyToTmpRegister(ty, src_mcv);4888 const reg = try self.copyToTmpRegister(ty, src_mcv);
4932 return self.genBinOpMir(mir_tag, ty, dst_mcv, .{ .register = reg });4889 return self.genBinOpMir(mir_tag, ty, dst_mcv, .{ .register = reg });
4933 },4890 },
4934 .stack_offset => |off| {4891 .stack_offset => |off| try self.asmRegisterMemory(
4935 try self.asmRegisterMemory(4892 mir_tag,
4936 mir_tag,4893 registerAlias(dst_reg, abi_size),
4937 registerAlias(dst_reg, abi_size),4894 Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = .rbp, .disp = -off }),
4938 Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = .rbp, .disp = -off }),4895 ),
4939 );
4940 },
4941 }4896 }
4942 },4897 },
4943 .memory, .linker_load, .stack_offset => {4898 .memory, .linker_load, .stack_offset => {
test/behavior/bugs/3007.zig-1
...@@ -21,7 +21,6 @@ fn get_foo() Foo.FooError!*Foo {...@@ -21,7 +21,6 @@ fn get_foo() Foo.FooError!*Foo {
21test "fixed" {21test "fixed" {
22 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;22 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
23 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO23 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
24 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
25 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO24 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
2625
27 default_foo = get_foo() catch null; // This Line26 default_foo = get_foo() catch null; // This Line
test/behavior/eval.zig-3
...@@ -967,7 +967,6 @@ test "closure capture type of runtime-known parameter" {...@@ -967,7 +967,6 @@ test "closure capture type of runtime-known parameter" {
967}967}
968968
969test "comptime break passing through runtime condition converted to runtime break" {969test "comptime break passing through runtime condition converted to runtime break" {
970 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
971 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO970 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
972971
973 const S = struct {972 const S = struct {
...@@ -999,7 +998,6 @@ test "comptime break passing through runtime condition converted to runtime brea...@@ -999,7 +998,6 @@ test "comptime break passing through runtime condition converted to runtime brea
999}998}
1000999
1001test "comptime break to outer loop passing through runtime condition converted to runtime break" {1000test "comptime break to outer loop passing through runtime condition converted to runtime break" {
1002 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1003 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1001 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1004 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1002 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1005 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1003 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -1218,7 +1216,6 @@ test "storing an array of type in a field" {...@@ -1218,7 +1216,6 @@ test "storing an array of type in a field" {
1218}1216}
12191217
1220test "pass pointer to field of comptime-only type as a runtime parameter" {1218test "pass pointer to field of comptime-only type as a runtime parameter" {
1221 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1222 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1219 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1223 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1220 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
12241221