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
180614180614fn airMemset(self: *CodeGen, inst: Air.Inst.Index, safety: bool) !void {
180615180615 const pt = self.pt;
180616180616 const zcu = pt.zcu;
180617 const ip = &zcu.intern_pool;
180617180618 const bin_op = self.air.instructions.items(.data)[@backingInt(inst)].bin_op;
180618180619
180619180620 result: {
......@@ -180645,29 +180646,32 @@ fn airMemset(self: *CodeGen, inst: Air.Inst.Index, safety: bool) !void {
180645180646
180646180647 const elem_abi_size: u31 = @intCast(elem_ty.abiSize(zcu));
180647180648
180648 if (elem_abi_size == 1) {
180649 const dst_ptr: MCValue = switch (dst_ty.ptrSize(zcu)) {
180650 .slice => switch (dst) {
180651 .register_pair => |dst_regs| .{ .register = dst_regs[0] },
180652 else => dst,
180649 const dst_ptr: MCValue, const len: MCValue = switch (dst_ty.ptrSize(zcu)) {
180650 .slice => switch (dst) {
180651 else => .{ dst, dst.address().offset(8).deref() },
180652 .register_pair => |dst_regs| .{
180653 .{ .register = dst_regs[0] },
180654 .{ .register = dst_regs[1] },
180653180655 },
180654 .one => dst,
180655 .c, .many => unreachable,
180656 };
180657 const len: MCValue = switch (dst_ty.ptrSize(zcu)) {
180658 .slice => switch (dst) {
180659 .register_pair => |dst_regs| .{ .register = dst_regs[1] },
180660 else => dst.address().offset(8).deref(),
180656 .load_uav => |uav| switch (ip.indexToKey(uav.val)) {
180657 else => unreachable,
180658 .undef => .{ .undef, .undef },
180659 .slice => |slice| .{
180660 try self.lowerValue(.fromInterned(slice.ptr)),
180661 try self.lowerValue(.fromInterned(slice.len)),
180662 },
180661180663 },
180662 .one => .{ .immediate = dst_ty.childType(zcu).arrayLen(zcu) },
180663 .c, .many => unreachable,
180664 };
180665 const len_lock: ?RegisterLock = switch (len) {
180666 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
180667 else => null,
180668 };
180669 defer if (len_lock) |lock| self.register_manager.unlockReg(lock);
180664 },
180665 .one => .{ dst, .{ .immediate = dst_ty.childType(zcu).arrayLen(zcu) } },
180666 .c, .many => unreachable,
180667 };
180668 const len_lock: ?RegisterLock = switch (len) {
180669 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
180670 else => null,
180671 };
180672 defer if (len_lock) |lock| self.register_manager.unlockReg(lock);
180670180673
180674 if (elem_abi_size == 1) {
180671180675 try self.genInlineMemset(dst_ptr, src_val, len, .{ .safety = safety });
180672180676 break :result;
180673180677 }
......@@ -180675,25 +180679,16 @@ fn airMemset(self: *CodeGen, inst: Air.Inst.Index, safety: bool) !void {
180675180679 // Store the first element, and then rely on memcpy copying forwards.
180676180680 // Length zero requires a runtime check - so we handle arrays specially
180677180681 // here to elide it.
180678 switch (dst_ty.ptrSize(zcu)) {
180679 .slice => {
180682 switch (len) {
180683 else => {
180680180684 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
180691180686 // Used to store the number of elements for comparison.
180692180687 // After comparison, updated to store number of bytes needed to copy.
180693180688 const len_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
180694180689 const len_mcv: MCValue = .{ .register = len_reg };
180695 const len_lock = self.register_manager.lockRegAssumeUnused(len_reg);
180696 defer self.register_manager.unlockReg(len_lock);
180690 const len_reg_lock = self.register_manager.lockRegAssumeUnused(len_reg);
180691 defer self.register_manager.unlockReg(len_reg_lock);
180697180692
180698180693 try self.spillEflagsIfOccupied();
180699180694 try self.genSetReg(len_reg, .usize, len, .{});
......@@ -180725,12 +180720,10 @@ fn airMemset(self: *CodeGen, inst: Air.Inst.Index, safety: bool) !void {
180725180720
180726180721 self.performReloc(skip_reloc);
180727180722 },
180728 .one => {
180723 .immediate => |len_imm| {
180729180724 const elem_ptr_ty = try pt.singleMutPtrType(elem_ty);
180730180725
180731 const len = dst_ty.childType(zcu).arrayLen(zcu);
180732
180733 assert(len != 0); // prevented by Sema
180726 assert(len_imm != 0); // prevented by Sema
180734180727 try self.store(elem_ptr_ty, dst, src_val, .{ .safety = safety });
180735180728
180736180729 const second_elem_ptr_reg =
......@@ -180745,10 +180738,9 @@ fn airMemset(self: *CodeGen, inst: Air.Inst.Index, safety: bool) !void {
180745180738 .off = elem_abi_size,
180746180739 } }, .{});
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) };
180749180742 try self.genInlineMemcpy(second_elem_ptr_mcv, dst, bytes_to_copy, .{ .no_alias = false });
180750180743 },
180751 .c, .many => unreachable,
180752180744 }
180753180745 }
180754180746 return self.finishAir(inst, .unreach, .{ bin_op.lhs, bin_op.rhs, .none });
......@@ -182771,17 +182763,27 @@ const Temp = struct {
182771182763 assert(limb_index == 0);
182772182764 new_temp_index.tracking(cg).* = .init(.{ .lea_nav = nav });
182773182765 },
182774 .load_uav => |uav| {
182775 const new_reg =
182776 try cg.register_manager.allocReg(new_temp_index.toIndex(), abi.RegisterClass.gp);
182777 new_temp_index.tracking(cg).* = .init(.{ .register = new_reg });
182778 try cg.asmRegisterMemory(.{ ._, .mov }, new_reg.to64(), .{
182779 .base = .{ .uav = uav },
182780 .mod = .{ .rm = .{
182781 .size = .qword,
182782 .disp = @as(u31, limb_index) * 8,
182783 } },
182784 });
182766 .load_uav => |uav| switch (cg.pt.zcu.intern_pool.indexToKey(uav.val)) {
182767 else => {
182768 const new_reg = try cg.register_manager.allocReg(
182769 new_temp_index.toIndex(),
182770 abi.RegisterClass.gp,
182771 );
182772 new_temp_index.tracking(cg).* = .init(.{ .register = new_reg });
182773 try cg.asmRegisterMemory(.{ ._, .mov }, new_reg.to64(), .{
182774 .base = .{ .uav = uav },
182775 .mod = .{ .rm = .{
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 }))),
182785182787 },
182786182788 .lea_uav => |uav| {
182787182789 assert(limb_index == 0);
......@@ -182870,15 +182872,11 @@ const Temp = struct {
182870182872 fn toLimb(temp: *Temp, limb_ty: Type, limb_index: u28, cg: *CodeGen) InnerError!void {
182871182873 switch (temp.unwrap(cg)) {
182872182874 .ref => {},
182873 .temp => |temp_index| {
182875 .temp => |temp_index| inplace: {
182874182876 const temp_tracking = temp_index.tracking(cg);
182875182877 switch (temp_tracking.short) {
182876 else => {},
182877 .register, .lea_frame, .lea_nav, .lea_uav, .lea_lazy_sym => {
182878 assert(limb_index == 0);
182879 cg.temp_type[@backingInt(temp_index)] = limb_ty;
182880 return;
182881 },
182878 else => break :inplace,
182879 .register, .lea_frame, .lea_nav, .lea_uav, .lea_lazy_sym => assert(limb_index == 0),
182882182880 .register_pair => |regs| {
182883182881 switch (temp_tracking.long) {
182884182882 .none, .reserved_frame => {},
......@@ -182888,8 +182886,6 @@ const Temp = struct {
182888182886 for (regs, 0..) |reg, reg_index| if (reg_index != limb_index)
182889182887 cg.register_manager.freeReg(reg);
182890182888 temp_tracking.* = .init(.{ .register = regs[limb_index] });
182891 cg.temp_type[@backingInt(temp_index)] = limb_ty;
182892 return;
182893182889 },
182894182890 .load_frame => |frame_addr| if (!frame_addr.index.isNamed()) {
182895182891 assert(std.meta.eql(temp_tracking.long.load_frame, frame_addr));
......@@ -182897,10 +182893,19 @@ const Temp = struct {
182897182893 .index = frame_addr.index,
182898182894 .off = frame_addr.off + @as(u31, limb_index) * 8,
182899182895 } });
182900 cg.temp_type[@backingInt(temp_index)] = limb_ty;
182901 return;
182896 },
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 }))),
182902182905 },
182903182906 }
182907 cg.temp_type[@backingInt(temp_index)] = limb_ty;
182908 return;
182904182909 },
182905182910 .err_ret_trace => unreachable,
182906182911 }
test/behavior/memcpy.zig+29
......@@ -180,3 +180,32 @@ test "@memcpy no sentinel source into sentinel destination" {
180180 S.doTheTest();
181181 comptime S.doTheTest();
182182}
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 {
170170 s.set("hello");
171171 if (!std.mem.eql(u8, s.buffer[0..8], "hehelloo")) @compileError("bad");
172172}
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" {
204204 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
205205
206206 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;
208211 };
209 try expect(S.buf[0] == 123);
210 @memset(&S.buf, 456);
211 try expect(S.buf[0] == 456);
212 @memset(&S.buf, S.buf[0] + 333);
213 try expect(S.buf[0] == 789);
212
213 try expect(S.array_u8[0] == 1);
214 @memset(&S.array_u8, 2);
215 try expect(S.array_u8[0] == 2);
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);
214232}
215233
216234test "@memset array of booleans" {