authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-09-18 00:46:56-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-09-18 00:48:40-04:00
logcc9447c103a540ae21582385ef33581c9719324e
tree339d0a75382733b6925c39c1a6c18dd01c2bd586
parenta209d26b89e7b1ece37edf66853dcba70387e7f2

x86_64: fix memset of global slice


4 files changed, 148 insertions(+), 67 deletions(-)

src/codegen/x86_64/CodeGen.zig+66-61
...@@ -180614,6 +180614,7 @@ fn airAtomicStore(self: *CodeGen, inst: Air.Inst.Index, order: std.lang.AtomicOr...@@ -180614,6 +180614,7 @@ fn airAtomicStore(self: *CodeGen, inst: Air.Inst.Index, order: std.lang.AtomicOr
180614fn airMemset(self: *CodeGen, inst: Air.Inst.Index, safety: bool) !void {180614fn airMemset(self: *CodeGen, inst: Air.Inst.Index, safety: bool) !void {
180615 const pt = self.pt;180615 const pt = self.pt;
180616 const zcu = pt.zcu;180616 const zcu = pt.zcu;
180617 const ip = &zcu.intern_pool;
180617 const bin_op = self.air.instructions.items(.data)[@backingInt(inst)].bin_op;180618 const bin_op = self.air.instructions.items(.data)[@backingInt(inst)].bin_op;
180618180619
180619 result: {180620 result: {
...@@ -180645,29 +180646,32 @@ fn airMemset(self: *CodeGen, inst: Air.Inst.Index, safety: bool) !void {...@@ -180645,29 +180646,32 @@ fn airMemset(self: *CodeGen, inst: Air.Inst.Index, safety: bool) !void {
180645180646
180646 const elem_abi_size: u31 = @intCast(elem_ty.abiSize(zcu));180647 const elem_abi_size: u31 = @intCast(elem_ty.abiSize(zcu));
180647180648
180648 if (elem_abi_size == 1) {180649 const dst_ptr: MCValue, const len: MCValue = switch (dst_ty.ptrSize(zcu)) {
180649 const dst_ptr: MCValue = switch (dst_ty.ptrSize(zcu)) {180650 .slice => switch (dst) {
180650 .slice => switch (dst) {180651 else => .{ dst, dst.address().offset(8).deref() },
180651 .register_pair => |dst_regs| .{ .register = dst_regs[0] },180652 .register_pair => |dst_regs| .{
180652 else => dst,180653 .{ .register = dst_regs[0] },
180654 .{ .register = dst_regs[1] },
180653 },180655 },
180654 .one => dst,180656 .load_uav => |uav| switch (ip.indexToKey(uav.val)) {
180655 .c, .many => unreachable,180657 else => unreachable,
180656 };180658 .undef => .{ .undef, .undef },
180657 const len: MCValue = switch (dst_ty.ptrSize(zcu)) {180659 .slice => |slice| .{
180658 .slice => switch (dst) {180660 try self.lowerValue(.fromInterned(slice.ptr)),
180659 .register_pair => |dst_regs| .{ .register = dst_regs[1] },180661 try self.lowerValue(.fromInterned(slice.len)),
180660 else => dst.address().offset(8).deref(),180662 },
180661 },180663 },
180662 .one => .{ .immediate = dst_ty.childType(zcu).arrayLen(zcu) },180664 },
180663 .c, .many => unreachable,180665 .one => .{ dst, .{ .immediate = dst_ty.childType(zcu).arrayLen(zcu) } },
180664 };180666 .c, .many => unreachable,
180665 const len_lock: ?RegisterLock = switch (len) {180667 };
180666 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),180668 const len_lock: ?RegisterLock = switch (len) {
180667 else => null,180669 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
180668 };180670 else => null,
180669 defer if (len_lock) |lock| self.register_manager.unlockReg(lock);180671 };
180672 defer if (len_lock) |lock| self.register_manager.unlockReg(lock);
180670180673
180674 if (elem_abi_size == 1) {
180671 try self.genInlineMemset(dst_ptr, src_val, len, .{ .safety = safety });180675 try self.genInlineMemset(dst_ptr, src_val, len, .{ .safety = safety });
180672 break :result;180676 break :result;
180673 }180677 }
...@@ -180675,25 +180679,16 @@ fn airMemset(self: *CodeGen, inst: Air.Inst.Index, safety: bool) !void {...@@ -180675,25 +180679,16 @@ fn airMemset(self: *CodeGen, inst: Air.Inst.Index, safety: bool) !void {
180675 // Store the first element, and then rely on memcpy copying forwards.180679 // Store the first element, and then rely on memcpy copying forwards.
180676 // Length zero requires a runtime check - so we handle arrays specially180680 // Length zero requires a runtime check - so we handle arrays specially
180677 // here to elide it.180681 // here to elide it.
180678 switch (dst_ty.ptrSize(zcu)) {180682 switch (len) {
180679 .slice => {180683 else => {
180680 const slice_ptr_ty = dst_ty.slicePtrFieldType(zcu);180684 const slice_ptr_ty = dst_ty.slicePtrFieldType(zcu);
180681180685
180682 const dst_ptr: MCValue = switch (dst) {
180683 .register_pair => |dst_regs| .{ .register = dst_regs[0] },
180684 else => dst,
180685 };
180686 const len: MCValue = switch (dst) {
180687 .register_pair => |dst_regs| .{ .register = dst_regs[1] },
180688 else => dst.address().offset(8).deref(),
180689 };
180690
180691 // Used to store the number of elements for comparison.180686 // Used to store the number of elements for comparison.
180692 // After comparison, updated to store number of bytes needed to copy.180687 // After comparison, updated to store number of bytes needed to copy.
180693 const len_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);180688 const len_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
180694 const len_mcv: MCValue = .{ .register = len_reg };180689 const len_mcv: MCValue = .{ .register = len_reg };
180695 const len_lock = self.register_manager.lockRegAssumeUnused(len_reg);180690 const len_reg_lock = self.register_manager.lockRegAssumeUnused(len_reg);
180696 defer self.register_manager.unlockReg(len_lock);180691 defer self.register_manager.unlockReg(len_reg_lock);
180697180692
180698 try self.spillEflagsIfOccupied();180693 try self.spillEflagsIfOccupied();
180699 try self.genSetReg(len_reg, .usize, len, .{});180694 try self.genSetReg(len_reg, .usize, len, .{});
...@@ -180725,12 +180720,10 @@ fn airMemset(self: *CodeGen, inst: Air.Inst.Index, safety: bool) !void {...@@ -180725,12 +180720,10 @@ fn airMemset(self: *CodeGen, inst: Air.Inst.Index, safety: bool) !void {
180725180720
180726 self.performReloc(skip_reloc);180721 self.performReloc(skip_reloc);
180727 },180722 },
180728 .one => {180723 .immediate => |len_imm| {
180729 const elem_ptr_ty = try pt.singleMutPtrType(elem_ty);180724 const elem_ptr_ty = try pt.singleMutPtrType(elem_ty);
180730180725
180731 const len = dst_ty.childType(zcu).arrayLen(zcu);180726 assert(len_imm != 0); // prevented by Sema
180732
180733 assert(len != 0); // prevented by Sema
180734 try self.store(elem_ptr_ty, dst, src_val, .{ .safety = safety });180727 try self.store(elem_ptr_ty, dst, src_val, .{ .safety = safety });
180735180728
180736 const second_elem_ptr_reg =180729 const second_elem_ptr_reg =
...@@ -180745,10 +180738,9 @@ fn airMemset(self: *CodeGen, inst: Air.Inst.Index, safety: bool) !void {...@@ -180745,10 +180738,9 @@ fn airMemset(self: *CodeGen, inst: Air.Inst.Index, safety: bool) !void {
180745 .off = elem_abi_size,180738 .off = elem_abi_size,
180746 } }, .{});180739 } }, .{});
180747180740
180748 const bytes_to_copy: MCValue = .{ .immediate = elem_abi_size * (len - 1) };180741 const bytes_to_copy: MCValue = .{ .immediate = elem_abi_size * (len_imm - 1) };
180749 try self.genInlineMemcpy(second_elem_ptr_mcv, dst, bytes_to_copy, .{ .no_alias = false });180742 try self.genInlineMemcpy(second_elem_ptr_mcv, dst, bytes_to_copy, .{ .no_alias = false });
180750 },180743 },
180751 .c, .many => unreachable,
180752 }180744 }
180753 }180745 }
180754 return self.finishAir(inst, .unreach, .{ bin_op.lhs, bin_op.rhs, .none });180746 return self.finishAir(inst, .unreach, .{ bin_op.lhs, bin_op.rhs, .none });
...@@ -182771,17 +182763,27 @@ const Temp = struct {...@@ -182771,17 +182763,27 @@ const Temp = struct {
182771 assert(limb_index == 0);182763 assert(limb_index == 0);
182772 new_temp_index.tracking(cg).* = .init(.{ .lea_nav = nav });182764 new_temp_index.tracking(cg).* = .init(.{ .lea_nav = nav });
182773 },182765 },
182774 .load_uav => |uav| {182766 .load_uav => |uav| switch (cg.pt.zcu.intern_pool.indexToKey(uav.val)) {
182775 const new_reg =182767 else => {
182776 try cg.register_manager.allocReg(new_temp_index.toIndex(), abi.RegisterClass.gp);182768 const new_reg = try cg.register_manager.allocReg(
182777 new_temp_index.tracking(cg).* = .init(.{ .register = new_reg });182769 new_temp_index.toIndex(),
182778 try cg.asmRegisterMemory(.{ ._, .mov }, new_reg.to64(), .{182770 abi.RegisterClass.gp,
182779 .base = .{ .uav = uav },182771 );
182780 .mod = .{ .rm = .{182772 new_temp_index.tracking(cg).* = .init(.{ .register = new_reg });
182781 .size = .qword,182773 try cg.asmRegisterMemory(.{ ._, .mov }, new_reg.to64(), .{
182782 .disp = @as(u31, limb_index) * 8,182774 .base = .{ .uav = uav },
182783 } },182775 .mod = .{ .rm = .{
182784 });182776 .size = .qword,
182777 .disp = @as(u31, limb_index) * 8,
182778 } },
182779 });
182780 },
182781 .slice => |slice| new_temp_index.tracking(cg).* =
182782 .init(try cg.lowerValue(.fromInterned(switch (limb_index) {
182783 else => unreachable,
182784 0 => slice.ptr,
182785 1 => slice.len,
182786 }))),
182785 },182787 },
182786 .lea_uav => |uav| {182788 .lea_uav => |uav| {
182787 assert(limb_index == 0);182789 assert(limb_index == 0);
...@@ -182870,15 +182872,11 @@ const Temp = struct {...@@ -182870,15 +182872,11 @@ const Temp = struct {
182870 fn toLimb(temp: *Temp, limb_ty: Type, limb_index: u28, cg: *CodeGen) InnerError!void {182872 fn toLimb(temp: *Temp, limb_ty: Type, limb_index: u28, cg: *CodeGen) InnerError!void {
182871 switch (temp.unwrap(cg)) {182873 switch (temp.unwrap(cg)) {
182872 .ref => {},182874 .ref => {},
182873 .temp => |temp_index| {182875 .temp => |temp_index| inplace: {
182874 const temp_tracking = temp_index.tracking(cg);182876 const temp_tracking = temp_index.tracking(cg);
182875 switch (temp_tracking.short) {182877 switch (temp_tracking.short) {
182876 else => {},182878 else => break :inplace,
182877 .register, .lea_frame, .lea_nav, .lea_uav, .lea_lazy_sym => {182879 .register, .lea_frame, .lea_nav, .lea_uav, .lea_lazy_sym => assert(limb_index == 0),
182878 assert(limb_index == 0);
182879 cg.temp_type[@backingInt(temp_index)] = limb_ty;
182880 return;
182881 },
182882 .register_pair => |regs| {182880 .register_pair => |regs| {
182883 switch (temp_tracking.long) {182881 switch (temp_tracking.long) {
182884 .none, .reserved_frame => {},182882 .none, .reserved_frame => {},
...@@ -182888,8 +182886,6 @@ const Temp = struct {...@@ -182888,8 +182886,6 @@ const Temp = struct {
182888 for (regs, 0..) |reg, reg_index| if (reg_index != limb_index)182886 for (regs, 0..) |reg, reg_index| if (reg_index != limb_index)
182889 cg.register_manager.freeReg(reg);182887 cg.register_manager.freeReg(reg);
182890 temp_tracking.* = .init(.{ .register = regs[limb_index] });182888 temp_tracking.* = .init(.{ .register = regs[limb_index] });
182891 cg.temp_type[@backingInt(temp_index)] = limb_ty;
182892 return;
182893 },182889 },
182894 .load_frame => |frame_addr| if (!frame_addr.index.isNamed()) {182890 .load_frame => |frame_addr| if (!frame_addr.index.isNamed()) {
182895 assert(std.meta.eql(temp_tracking.long.load_frame, frame_addr));182891 assert(std.meta.eql(temp_tracking.long.load_frame, frame_addr));
...@@ -182897,10 +182893,19 @@ const Temp = struct {...@@ -182897,10 +182893,19 @@ const Temp = struct {
182897 .index = frame_addr.index,182893 .index = frame_addr.index,
182898 .off = frame_addr.off + @as(u31, limb_index) * 8,182894 .off = frame_addr.off + @as(u31, limb_index) * 8,
182899 } });182895 } });
182900 cg.temp_type[@backingInt(temp_index)] = limb_ty;182896 },
182901 return;182897 .load_uav => |uav| switch (cg.pt.zcu.intern_pool.indexToKey(uav.val)) {
182898 else => break :inplace,
182899 .slice => |slice| temp_tracking.* =
182900 .init(try cg.lowerValue(.fromInterned(switch (limb_index) {
182901 else => unreachable,
182902 0 => slice.ptr,
182903 1 => slice.len,
182904 }))),
182902 },182905 },
182903 }182906 }
182907 cg.temp_type[@backingInt(temp_index)] = limb_ty;
182908 return;
182904 },182909 },
182905 .err_ret_trace => unreachable,182910 .err_ret_trace => unreachable,
182906 }182911 }
test/behavior/memcpy.zig+29
...@@ -180,3 +180,32 @@ test "@memcpy no sentinel source into sentinel destination" {...@@ -180,3 +180,32 @@ test "@memcpy no sentinel source into sentinel destination" {
180 S.doTheTest();180 S.doTheTest();
181 comptime S.doTheTest();181 comptime S.doTheTest();
182}182}
183
184test "@memcpy a global array" {
185 const S = struct {
186 var array_u8: [1]u8 = .{1};
187 const slice_u8: []u8 = &array_u8;
188 var array_u32: [1]u32 = .{10};
189 const slice_u32: []u32 = &array_u32;
190 };
191
192 try expect(S.array_u8[0] == 1);
193 @memcpy(&S.array_u8, &[1]u8{2});
194 try expect(S.array_u8[0] == 2);
195 @memcpy(&S.array_u8, &[1]u8{S.array_u8[0] + 1});
196 try expect(S.array_u8[0] == 3);
197 @memcpy(S.slice_u8, &[1]u8{4});
198 try expect(S.array_u8[0] == 4);
199 @memcpy(S.slice_u8, &[1]u8{S.array_u8[0] + 1});
200 try expect(S.array_u8[0] == 5);
201
202 try expect(S.array_u32[0] == 10);
203 @memcpy(&S.array_u32, &[1]u32{20});
204 try expect(S.array_u32[0] == 20);
205 @memcpy(&S.array_u32, &[1]u32{S.array_u32[0] + 10});
206 try expect(S.array_u32[0] == 30);
207 @memcpy(S.slice_u32, &[1]u32{40});
208 try expect(S.array_u32[0] == 40);
209 @memcpy(S.slice_u32, &[1]u32{S.array_u32[0] + 10});
210 try expect(S.array_u32[0] == 50);
211}
test/behavior/memmove.zig+29
...@@ -170,3 +170,32 @@ comptime {...@@ -170,3 +170,32 @@ comptime {
170 s.set("hello");170 s.set("hello");
171 if (!std.mem.eql(u8, s.buffer[0..8], "hehelloo")) @compileError("bad");171 if (!std.mem.eql(u8, s.buffer[0..8], "hehelloo")) @compileError("bad");
172}172}
173
174test "@memmove a global array" {
175 const S = struct {
176 var array_u8: [1]u8 = .{1};
177 const slice_u8: []u8 = &array_u8;
178 var array_u32: [1]u32 = .{10};
179 const slice_u32: []u32 = &array_u32;
180 };
181
182 try expect(S.array_u8[0] == 1);
183 @memmove(&S.array_u8, &[1]u8{2});
184 try expect(S.array_u8[0] == 2);
185 @memmove(&S.array_u8, &[1]u8{S.array_u8[0] + 1});
186 try expect(S.array_u8[0] == 3);
187 @memmove(S.slice_u8, &[1]u8{4});
188 try expect(S.array_u8[0] == 4);
189 @memmove(S.slice_u8, &[1]u8{S.array_u8[0] + 1});
190 try expect(S.array_u8[0] == 5);
191
192 try expect(S.array_u32[0] == 10);
193 @memmove(&S.array_u32, &[1]u32{20});
194 try expect(S.array_u32[0] == 20);
195 @memmove(&S.array_u32, &[1]u32{S.array_u32[0] + 10});
196 try expect(S.array_u32[0] == 30);
197 @memmove(S.slice_u32, &[1]u32{40});
198 try expect(S.array_u32[0] == 40);
199 @memmove(S.slice_u32, &[1]u32{S.array_u32[0] + 10});
200 try expect(S.array_u32[0] == 50);
201}
test/behavior/memset.zig+24-6
...@@ -204,13 +204,31 @@ test "@memset a global array" {...@@ -204,13 +204,31 @@ test "@memset a global array" {
204 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;204 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
205205
206 const S = struct {206 const S = struct {
207 var buf: [1]u32 = .{123};207 var array_u8: [1]u8 = .{1};
208 const slice_u8: []u8 = &array_u8;
209 var array_u32: [1]u32 = .{10};
210 const slice_u32: []u32 = &array_u32;
208 };211 };
209 try expect(S.buf[0] == 123);212
210 @memset(&S.buf, 456);213 try expect(S.array_u8[0] == 1);
211 try expect(S.buf[0] == 456);214 @memset(&S.array_u8, 2);
212 @memset(&S.buf, S.buf[0] + 333);215 try expect(S.array_u8[0] == 2);
213 try expect(S.buf[0] == 789);216 @memset(&S.array_u8, S.array_u8[0] + 1);
217 try expect(S.array_u8[0] == 3);
218 @memset(S.slice_u8, 4);
219 try expect(S.array_u8[0] == 4);
220 @memset(S.slice_u8, S.array_u8[0] + 1);
221 try expect(S.array_u8[0] == 5);
222
223 try expect(S.array_u32[0] == 10);
224 @memset(&S.array_u32, 20);
225 try expect(S.array_u32[0] == 20);
226 @memset(&S.array_u32, S.array_u32[0] + 10);
227 try expect(S.array_u32[0] == 30);
228 @memset(S.slice_u32, 40);
229 try expect(S.array_u32[0] == 40);
230 @memset(S.slice_u32, S.array_u32[0] + 10);
231 try expect(S.array_u32[0] == 50);
214}232}
215233
216test "@memset array of booleans" {234test "@memset array of booleans" {