From a89d242891270885bf8cb138d40d860315d02bb3 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 16 Sep 2026 21:25:03 -0400 Subject: [PATCH 1/3] x86_64: handle OPV args Closes #35194 --- src/codegen/x86_64/CodeGen.zig | 47 ++++++++++++++-------------------- src/codegen/x86_64/Emit.zig | 7 ----- src/codegen/x86_64/Lower.zig | 2 -- src/codegen/x86_64/Mir.zig | 6 ----- 4 files changed, 19 insertions(+), 43 deletions(-) diff --git a/src/codegen/x86_64/CodeGen.zig b/src/codegen/x86_64/CodeGen.zig index 62c3c1a41bc51e3a1fdb85b38309fafbd3d28412..c3b548b58a7ae0624b7bbea3c25fb93794b2ae9d 100644 --- a/src/codegen/x86_64/CodeGen.zig +++ b/src/codegen/x86_64/CodeGen.zig @@ -1224,9 +1224,7 @@ fn formatWipMir(data: FormatWipMirData, w: *Writer) Writer.Error!void { .pseudo_dbg_enter_block_none, .pseudo_dbg_leave_block_none, .pseudo_dbg_end_none, - .pseudo_dbg_arg_none, .pseudo_dbg_var_args_none, - .pseudo_dbg_var_none, .pseudo_dead_none, => {}, .pseudo_dbg_line_stmt_line_column, @@ -2318,39 +2316,32 @@ fn genMainBody( }; defer zir_param_index += 1; - if (comptime_args.len > 0) switch (comptime_args.get(ip)[zir_param_index]) { - .none => {}, - else => |comptime_arg| { - try cg.mir_locals.append(cg.gpa, .{ .name = name, .type = ip.typeOf(comptime_arg) }); - _ = try cg.addInst(.{ - .tag = .pseudo, - .ops = .pseudo_dbg_arg_val, - .data = .{ .ip_index = comptime_arg }, - }); - continue; + const arg_ty: Type, const arg_val: ?Value = arg: switch (if (comptime_args.len > 0) + comptime_args.get(ip)[zir_param_index] + else + .none) { + else => |arg_val| .{ .fromInterned(ip.typeOf(arg_val)), .fromInterned(arg_val) }, + .none => { + const arg_ty: Type = .fromInterned(fn_info.param_types.get(ip)[fn_param_index]); + fn_param_index += 1; + break :arg .{ arg_ty, try arg_ty.onePossibleValue(pt) }; }, }; - - const arg_ty = fn_info.param_types.get(ip)[fn_param_index]; - try cg.mir_locals.append(cg.gpa, .{ .name = name, .type = arg_ty }); - fn_param_index += 1; - - if (air_arg_index == air_args_body.len) { - try cg.asmPseudo(.pseudo_dbg_arg_none); + try cg.mir_locals.append(cg.gpa, .{ .name = name, .type = arg_ty.toIntern() }); + if (arg_val) |val| { + _ = try cg.addInst(.{ + .tag = .pseudo, + .ops = .pseudo_dbg_arg_val, + .data = .{ .ip_index = val.toIntern() }, + }); continue; } + const air_arg_inst = air_args_body[air_arg_index]; const air_arg_data = cg.air.instructions.items(.data)[air_arg_index].arg; - if (air_arg_data.zir_param_index != zir_param_index) { - try cg.asmPseudo(.pseudo_dbg_arg_none); - continue; - } air_arg_index += 1; - try cg.genLocalDebugInfo( - .arg, - .fromInterned(arg_ty), - cg.getResolvedInstValue(air_arg_inst).short, - ); + assert(air_arg_data.zir_param_index == zir_param_index); + try cg.genLocalDebugInfo(.arg, arg_ty, cg.getResolvedInstValue(air_arg_inst).short); } if (fn_info.is_var_args) try cg.asmPseudo(.pseudo_dbg_var_args_none); } diff --git a/src/codegen/x86_64/Emit.zig b/src/codegen/x86_64/Emit.zig index 0955a8d91b10ad9687fb522e76db3a03778258f0..eddc1f1b9341485ac0802140bbe0f1efbd08efa7 100644 --- a/src/codegen/x86_64/Emit.zig +++ b/src/codegen/x86_64/Emit.zig @@ -536,14 +536,12 @@ pub fn emitMir(emit: *Emit) Error!void { .column = emit.prev_di_loc.column, .end = true, }), - .pseudo_dbg_arg_none, .pseudo_dbg_arg_i_s, .pseudo_dbg_arg_i_u, .pseudo_dbg_arg_i_64, .pseudo_dbg_arg_ro, .pseudo_dbg_arg_fa, .pseudo_dbg_arg_m, - .pseudo_dbg_var_none, .pseudo_dbg_var_i_s, .pseudo_dbg_var_i_u, .pseudo_dbg_var_i_64, @@ -560,7 +558,6 @@ pub fn emitMir(emit: *Emit) Error!void { var loc_buf: [2]DwarfLoc = undefined; const loc: DwarfLoc = loc: switch (mir_inst.ops) { else => unreachable, - .pseudo_dbg_arg_none, .pseudo_dbg_var_none => .empty, .pseudo_dbg_arg_i_s, .pseudo_dbg_arg_i_u, .pseudo_dbg_var_i_s, @@ -632,23 +629,19 @@ pub fn emitMir(emit: *Emit) Error!void { try dwarf.genLocalVarDebugInfo( switch (mir_inst.ops) { else => unreachable, - .pseudo_dbg_arg_none, .pseudo_dbg_arg_i_s, .pseudo_dbg_arg_i_u, .pseudo_dbg_arg_i_64, .pseudo_dbg_arg_ro, .pseudo_dbg_arg_fa, .pseudo_dbg_arg_m, - .pseudo_dbg_arg_val, => .arg, - .pseudo_dbg_var_none, .pseudo_dbg_var_i_s, .pseudo_dbg_var_i_u, .pseudo_dbg_var_i_64, .pseudo_dbg_var_ro, .pseudo_dbg_var_fa, .pseudo_dbg_var_m, - .pseudo_dbg_var_val, => .local_var, }, local.name.toSlice(&emit.lower.mir), diff --git a/src/codegen/x86_64/Lower.zig b/src/codegen/x86_64/Lower.zig index db93e40a1dd04cf6a2076b06fda4cd64fc2fe017..3763128a7ac707381fb8d54f0d4116658fe959a9 100644 --- a/src/codegen/x86_64/Lower.zig +++ b/src/codegen/x86_64/Lower.zig @@ -320,7 +320,6 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct { .pseudo_dbg_enter_inline_func, .pseudo_dbg_leave_inline_func, .pseudo_dbg_end_none, - .pseudo_dbg_arg_none, .pseudo_dbg_arg_i_s, .pseudo_dbg_arg_i_u, .pseudo_dbg_arg_i_64, @@ -329,7 +328,6 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct { .pseudo_dbg_arg_m, .pseudo_dbg_arg_val, .pseudo_dbg_var_args_none, - .pseudo_dbg_var_none, .pseudo_dbg_var_i_s, .pseudo_dbg_var_i_u, .pseudo_dbg_var_i_64, diff --git a/src/codegen/x86_64/Mir.zig b/src/codegen/x86_64/Mir.zig index eb56298da4647617101a8339a89112ec6884e649..1b2d3cb63ebef6d025cb77cc109a4ddce7536e7b 100644 --- a/src/codegen/x86_64/Mir.zig +++ b/src/codegen/x86_64/Mir.zig @@ -1547,9 +1547,6 @@ pub const Inst = struct { /// Uses `none` payload. pseudo_dbg_end_none, /// Local argument. - /// Uses `none` payload. - pseudo_dbg_arg_none, - /// Local argument. /// Uses `i` payload. pseudo_dbg_arg_i_s, /// Local argument. @@ -1573,9 +1570,6 @@ pub const Inst = struct { /// Remaining arguments are varargs. pseudo_dbg_var_args_none, /// Local variable. - /// Uses `none` payload. - pseudo_dbg_var_none, - /// Local variable. /// Uses `i` payload. pseudo_dbg_var_i_s, /// Local variable. -- 2.54.0 From a209d26b89e7b1ece37edf66853dcba70387e7f2 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 17 Sep 2026 23:45:41 -0400 Subject: [PATCH 2/3] x86_64: fix missing eflags clobber Closes #36871 --- src/codegen/x86_64/CodeGen.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/codegen/x86_64/CodeGen.zig b/src/codegen/x86_64/CodeGen.zig index c3b548b58a7ae0624b7bbea3c25fb93794b2ae9d..ef8a83e3f1cddce79fe3e9bce2e18817d6826ac6 100644 --- a/src/codegen/x86_64/CodeGen.zig +++ b/src/codegen/x86_64/CodeGen.zig @@ -180695,6 +180695,7 @@ fn airMemset(self: *CodeGen, inst: Air.Inst.Index, safety: bool) !void { const len_lock = self.register_manager.lockRegAssumeUnused(len_reg); defer self.register_manager.unlockReg(len_lock); + try self.spillEflagsIfOccupied(); try self.genSetReg(len_reg, .usize, len, .{}); try self.asmRegisterRegister(.{ ._, .@"test" }, len_reg, len_reg); -- 2.54.0 From cc9447c103a540ae21582385ef33581c9719324e Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Fri, 18 Sep 2026 00:46:56 -0400 Subject: [PATCH 3/3] 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