From cc9447c103a540ae21582385ef33581c9719324e Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Fri, 18 Sep 2026 00:46:56 -0400 Subject: [PATCH] x86_64: fix memset of global slice --- src/codegen/x86_64/CodeGen.zig | 131 +++++++++++++++++---------------- test/behavior/memcpy.zig | 29 ++++++++ test/behavior/memmove.zig | 29 ++++++++ test/behavior/memset.zig | 30 ++++++-- 4 files changed, 150 insertions(+), 69 deletions(-) diff --git a/src/codegen/x86_64/CodeGen.zig b/src/codegen/x86_64/CodeGen.zig index ef8a83e3f1cddce79fe3e9bce2e18817d6826ac6..e152d984948b4097dc2f7fec3847ba5e7d775672 100644 --- a/src/codegen/x86_64/CodeGen.zig +++ b/src/codegen/x86_64/CodeGen.zig @@ -180614,6 +180614,7 @@ fn airAtomicStore(self: *CodeGen, inst: Air.Inst.Index, order: std.lang.AtomicOr fn airMemset(self: *CodeGen, inst: Air.Inst.Index, safety: bool) !void { const pt = self.pt; const zcu = pt.zcu; + const ip = &zcu.intern_pool; const bin_op = self.air.instructions.items(.data)[@backingInt(inst)].bin_op; result: { @@ -180645,29 +180646,32 @@ fn airMemset(self: *CodeGen, inst: Air.Inst.Index, safety: bool) !void { const elem_abi_size: u31 = @intCast(elem_ty.abiSize(zcu)); + const dst_ptr: MCValue, const len: MCValue = switch (dst_ty.ptrSize(zcu)) { + .slice => switch (dst) { + else => .{ dst, dst.address().offset(8).deref() }, + .register_pair => |dst_regs| .{ + .{ .register = dst_regs[0] }, + .{ .register = dst_regs[1] }, + }, + .load_uav => |uav| switch (ip.indexToKey(uav.val)) { + else => unreachable, + .undef => .{ .undef, .undef }, + .slice => |slice| .{ + try self.lowerValue(.fromInterned(slice.ptr)), + try self.lowerValue(.fromInterned(slice.len)), + }, + }, + }, + .one => .{ dst, .{ .immediate = dst_ty.childType(zcu).arrayLen(zcu) } }, + .c, .many => unreachable, + }; + const len_lock: ?RegisterLock = switch (len) { + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), + else => null, + }; + defer if (len_lock) |lock| self.register_manager.unlockReg(lock); + if (elem_abi_size == 1) { - const dst_ptr: MCValue = switch (dst_ty.ptrSize(zcu)) { - .slice => switch (dst) { - .register_pair => |dst_regs| .{ .register = dst_regs[0] }, - else => dst, - }, - .one => dst, - .c, .many => unreachable, - }; - const len: MCValue = switch (dst_ty.ptrSize(zcu)) { - .slice => switch (dst) { - .register_pair => |dst_regs| .{ .register = dst_regs[1] }, - else => dst.address().offset(8).deref(), - }, - .one => .{ .immediate = dst_ty.childType(zcu).arrayLen(zcu) }, - .c, .many => unreachable, - }; - const len_lock: ?RegisterLock = switch (len) { - .register => |reg| self.register_manager.lockRegAssumeUnused(reg), - else => null, - }; - defer if (len_lock) |lock| self.register_manager.unlockReg(lock); - try self.genInlineMemset(dst_ptr, src_val, len, .{ .safety = safety }); break :result; } @@ -180675,25 +180679,16 @@ fn airMemset(self: *CodeGen, inst: Air.Inst.Index, safety: bool) !void { // Store the first element, and then rely on memcpy copying forwards. // Length zero requires a runtime check - so we handle arrays specially // here to elide it. - switch (dst_ty.ptrSize(zcu)) { - .slice => { + switch (len) { + else => { const slice_ptr_ty = dst_ty.slicePtrFieldType(zcu); - const dst_ptr: MCValue = switch (dst) { - .register_pair => |dst_regs| .{ .register = dst_regs[0] }, - else => dst, - }; - const len: MCValue = switch (dst) { - .register_pair => |dst_regs| .{ .register = dst_regs[1] }, - else => dst.address().offset(8).deref(), - }; - // Used to store the number of elements for comparison. // After comparison, updated to store number of bytes needed to copy. const len_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); const len_mcv: MCValue = .{ .register = len_reg }; - const len_lock = self.register_manager.lockRegAssumeUnused(len_reg); - defer self.register_manager.unlockReg(len_lock); + const len_reg_lock = self.register_manager.lockRegAssumeUnused(len_reg); + defer self.register_manager.unlockReg(len_reg_lock); try self.spillEflagsIfOccupied(); try self.genSetReg(len_reg, .usize, len, .{}); @@ -180725,12 +180720,10 @@ fn airMemset(self: *CodeGen, inst: Air.Inst.Index, safety: bool) !void { self.performReloc(skip_reloc); }, - .one => { + .immediate => |len_imm| { const elem_ptr_ty = try pt.singleMutPtrType(elem_ty); - const len = dst_ty.childType(zcu).arrayLen(zcu); - - assert(len != 0); // prevented by Sema + assert(len_imm != 0); // prevented by Sema try self.store(elem_ptr_ty, dst, src_val, .{ .safety = safety }); const second_elem_ptr_reg = @@ -180745,10 +180738,9 @@ fn airMemset(self: *CodeGen, inst: Air.Inst.Index, safety: bool) !void { .off = elem_abi_size, } }, .{}); - const bytes_to_copy: MCValue = .{ .immediate = elem_abi_size * (len - 1) }; + const bytes_to_copy: MCValue = .{ .immediate = elem_abi_size * (len_imm - 1) }; try self.genInlineMemcpy(second_elem_ptr_mcv, dst, bytes_to_copy, .{ .no_alias = false }); }, - .c, .many => unreachable, } } return self.finishAir(inst, .unreach, .{ bin_op.lhs, bin_op.rhs, .none }); @@ -182771,17 +182763,27 @@ const Temp = struct { assert(limb_index == 0); new_temp_index.tracking(cg).* = .init(.{ .lea_nav = nav }); }, - .load_uav => |uav| { - const new_reg = - try cg.register_manager.allocReg(new_temp_index.toIndex(), abi.RegisterClass.gp); - new_temp_index.tracking(cg).* = .init(.{ .register = new_reg }); - try cg.asmRegisterMemory(.{ ._, .mov }, new_reg.to64(), .{ - .base = .{ .uav = uav }, - .mod = .{ .rm = .{ - .size = .qword, - .disp = @as(u31, limb_index) * 8, - } }, - }); + .load_uav => |uav| switch (cg.pt.zcu.intern_pool.indexToKey(uav.val)) { + else => { + const new_reg = try cg.register_manager.allocReg( + new_temp_index.toIndex(), + abi.RegisterClass.gp, + ); + new_temp_index.tracking(cg).* = .init(.{ .register = new_reg }); + try cg.asmRegisterMemory(.{ ._, .mov }, new_reg.to64(), .{ + .base = .{ .uav = uav }, + .mod = .{ .rm = .{ + .size = .qword, + .disp = @as(u31, limb_index) * 8, + } }, + }); + }, + .slice => |slice| new_temp_index.tracking(cg).* = + .init(try cg.lowerValue(.fromInterned(switch (limb_index) { + else => unreachable, + 0 => slice.ptr, + 1 => slice.len, + }))), }, .lea_uav => |uav| { assert(limb_index == 0); @@ -182870,15 +182872,11 @@ const Temp = struct { fn toLimb(temp: *Temp, limb_ty: Type, limb_index: u28, cg: *CodeGen) InnerError!void { switch (temp.unwrap(cg)) { .ref => {}, - .temp => |temp_index| { + .temp => |temp_index| inplace: { const temp_tracking = temp_index.tracking(cg); switch (temp_tracking.short) { - else => {}, - .register, .lea_frame, .lea_nav, .lea_uav, .lea_lazy_sym => { - assert(limb_index == 0); - cg.temp_type[@backingInt(temp_index)] = limb_ty; - return; - }, + else => break :inplace, + .register, .lea_frame, .lea_nav, .lea_uav, .lea_lazy_sym => assert(limb_index == 0), .register_pair => |regs| { switch (temp_tracking.long) { .none, .reserved_frame => {}, @@ -182888,8 +182886,6 @@ const Temp = struct { for (regs, 0..) |reg, reg_index| if (reg_index != limb_index) cg.register_manager.freeReg(reg); temp_tracking.* = .init(.{ .register = regs[limb_index] }); - cg.temp_type[@backingInt(temp_index)] = limb_ty; - return; }, .load_frame => |frame_addr| if (!frame_addr.index.isNamed()) { assert(std.meta.eql(temp_tracking.long.load_frame, frame_addr)); @@ -182897,10 +182893,19 @@ const Temp = struct { .index = frame_addr.index, .off = frame_addr.off + @as(u31, limb_index) * 8, } }); - cg.temp_type[@backingInt(temp_index)] = limb_ty; - return; + }, + .load_uav => |uav| switch (cg.pt.zcu.intern_pool.indexToKey(uav.val)) { + else => break :inplace, + .slice => |slice| temp_tracking.* = + .init(try cg.lowerValue(.fromInterned(switch (limb_index) { + else => unreachable, + 0 => slice.ptr, + 1 => slice.len, + }))), }, } + cg.temp_type[@backingInt(temp_index)] = limb_ty; + return; }, .err_ret_trace => unreachable, } diff --git a/test/behavior/memcpy.zig b/test/behavior/memcpy.zig index 462ce44b1b4720971aa735230692a07165eabb73..cd2cdeda583c4c986271094d71957344044223fe 100644 --- a/test/behavior/memcpy.zig +++ b/test/behavior/memcpy.zig @@ -180,3 +180,32 @@ test "@memcpy no sentinel source into sentinel destination" { S.doTheTest(); comptime S.doTheTest(); } + +test "@memcpy a global array" { + const S = struct { + var array_u8: [1]u8 = .{1}; + const slice_u8: []u8 = &array_u8; + var array_u32: [1]u32 = .{10}; + const slice_u32: []u32 = &array_u32; + }; + + try expect(S.array_u8[0] == 1); + @memcpy(&S.array_u8, &[1]u8{2}); + try expect(S.array_u8[0] == 2); + @memcpy(&S.array_u8, &[1]u8{S.array_u8[0] + 1}); + try expect(S.array_u8[0] == 3); + @memcpy(S.slice_u8, &[1]u8{4}); + try expect(S.array_u8[0] == 4); + @memcpy(S.slice_u8, &[1]u8{S.array_u8[0] + 1}); + try expect(S.array_u8[0] == 5); + + try expect(S.array_u32[0] == 10); + @memcpy(&S.array_u32, &[1]u32{20}); + try expect(S.array_u32[0] == 20); + @memcpy(&S.array_u32, &[1]u32{S.array_u32[0] + 10}); + try expect(S.array_u32[0] == 30); + @memcpy(S.slice_u32, &[1]u32{40}); + try expect(S.array_u32[0] == 40); + @memcpy(S.slice_u32, &[1]u32{S.array_u32[0] + 10}); + try expect(S.array_u32[0] == 50); +} diff --git a/test/behavior/memmove.zig b/test/behavior/memmove.zig index ad520989c23252b291e6841d08a9107c0cfa6eab..c2a8d7427098483506ae7f4ba7a9b11cfcc45e3c 100644 --- a/test/behavior/memmove.zig +++ b/test/behavior/memmove.zig @@ -170,3 +170,32 @@ comptime { s.set("hello"); if (!std.mem.eql(u8, s.buffer[0..8], "hehelloo")) @compileError("bad"); } + +test "@memmove a global array" { + const S = struct { + var array_u8: [1]u8 = .{1}; + const slice_u8: []u8 = &array_u8; + var array_u32: [1]u32 = .{10}; + const slice_u32: []u32 = &array_u32; + }; + + try expect(S.array_u8[0] == 1); + @memmove(&S.array_u8, &[1]u8{2}); + try expect(S.array_u8[0] == 2); + @memmove(&S.array_u8, &[1]u8{S.array_u8[0] + 1}); + try expect(S.array_u8[0] == 3); + @memmove(S.slice_u8, &[1]u8{4}); + try expect(S.array_u8[0] == 4); + @memmove(S.slice_u8, &[1]u8{S.array_u8[0] + 1}); + try expect(S.array_u8[0] == 5); + + try expect(S.array_u32[0] == 10); + @memmove(&S.array_u32, &[1]u32{20}); + try expect(S.array_u32[0] == 20); + @memmove(&S.array_u32, &[1]u32{S.array_u32[0] + 10}); + try expect(S.array_u32[0] == 30); + @memmove(S.slice_u32, &[1]u32{40}); + try expect(S.array_u32[0] == 40); + @memmove(S.slice_u32, &[1]u32{S.array_u32[0] + 10}); + try expect(S.array_u32[0] == 50); +} diff --git a/test/behavior/memset.zig b/test/behavior/memset.zig index 95005288f36f5eddb6001a7d8e543c29ddab905a..ffe62f20352b5edd7d9748b90970289e223e1505 100644 --- a/test/behavior/memset.zig +++ b/test/behavior/memset.zig @@ -204,13 +204,31 @@ test "@memset a global array" { if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; const S = struct { - var buf: [1]u32 = .{123}; + var array_u8: [1]u8 = .{1}; + const slice_u8: []u8 = &array_u8; + var array_u32: [1]u32 = .{10}; + const slice_u32: []u32 = &array_u32; }; - try expect(S.buf[0] == 123); - @memset(&S.buf, 456); - try expect(S.buf[0] == 456); - @memset(&S.buf, S.buf[0] + 333); - try expect(S.buf[0] == 789); + + try expect(S.array_u8[0] == 1); + @memset(&S.array_u8, 2); + try expect(S.array_u8[0] == 2); + @memset(&S.array_u8, S.array_u8[0] + 1); + try expect(S.array_u8[0] == 3); + @memset(S.slice_u8, 4); + try expect(S.array_u8[0] == 4); + @memset(S.slice_u8, S.array_u8[0] + 1); + try expect(S.array_u8[0] == 5); + + try expect(S.array_u32[0] == 10); + @memset(&S.array_u32, 20); + try expect(S.array_u32[0] == 20); + @memset(&S.array_u32, S.array_u32[0] + 10); + try expect(S.array_u32[0] == 30); + @memset(S.slice_u32, 40); + try expect(S.array_u32[0] == 40); + @memset(S.slice_u32, S.array_u32[0] + 10); + try expect(S.array_u32[0] == 50); } test "@memset array of booleans" { -- 2.54.0