authorgravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2026-10-23 17:04:46-05:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-10-25 01:47:06+02:00
logdd0fbcc6027c19fdb195aa32101475f50a3fbc75
treee8e81c3063e0f0e8a9691eafea9947f90188bb51
parentdaed36ce0f883a7298aefc1857fbb190cc6efde0
signature Signed by SSH key SHA256:7B/LJ7bpR1eX8aCXSr4mtd5M45VMPKcx9zY8e95b5QM

x86_64: rewrite cmpxchg_{weak,strong} implementation


2 files changed, 223 insertions(+), 125 deletions(-)

src/codegen/x86_64/CodeGen.zig+216-125
......@@ -210,6 +210,8 @@ pub const MCValue = union(enum) {
210210 register_offset: bits.RegisterOffset,
211211 /// The value is a tuple { wrapped, overflow } where wrapped value is stored in the GP register.
212212 register_overflow: struct { reg: Register, eflags: Condition },
213 /// The value is a tuple { wrapped, overflow } where wrapped value is stored across two registers.
214 register_pair_overflow: struct { pair: [2]Register, eflags: Condition },
213215 /// The value is a bool vector stored in a vector register with a different scalar type.
214216 register_mask: Mask,
215217 /// The value is in memory at a hard-coded address.
......@@ -265,6 +267,7 @@ pub const MCValue = union(enum) {
265267 .register_mask,
266268 .eflags,
267269 .register_overflow,
270 .register_pair_overflow,
268271 .indirect_load_frame,
269272 .lea_frame,
270273 .lea_nav,
......@@ -363,6 +366,7 @@ pub const MCValue = union(enum) {
363366 .indirect,
364367 .indirect_mask,
365368 => |*pl| (&pl.reg)[0..1],
369 .register_pair_overflow => |*rpo| &rpo.pair,
366370 else => &.{},
367371 };
368372 }
......@@ -371,6 +375,7 @@ pub const MCValue = union(enum) {
371375 return switch (mcv) {
372376 .eflags => |cc| cc,
373377 .register_overflow => |reg_ov| reg_ov.eflags,
378 .register_pair_overflow => |rpo| rpo.eflags,
374379 else => null,
375380 };
376381 }
......@@ -396,6 +401,7 @@ pub const MCValue = union(enum) {
396401 .register_quadruple,
397402 .register_offset,
398403 .register_overflow,
404 .register_pair_overflow,
399405 .register_mask,
400406 .indirect_mask,
401407 .lea_frame,
......@@ -437,6 +443,7 @@ pub const MCValue = union(enum) {
437443 .register_triple,
438444 .register_quadruple,
439445 .register_overflow,
446 .register_pair_overflow,
440447 .register_mask,
441448 .memory,
442449 .indirect,
......@@ -487,6 +494,7 @@ pub const MCValue = union(enum) {
487494 .register_triple,
488495 .register_quadruple,
489496 .register_overflow,
497 .register_pair_overflow,
490498 .register_mask,
491499 .memory,
492500 .indirect,
......@@ -530,6 +538,7 @@ pub const MCValue = union(enum) {
530538 .register_quadruple,
531539 .register_offset,
532540 .register_overflow,
541 .register_pair_overflow,
533542 .register_mask,
534543 .indirect_load_frame,
535544 .lea_frame,
......@@ -604,6 +613,11 @@ pub const MCValue = union(enum) {
604613 @tagName(pl.eflags),
605614 @tagName(pl.reg),
606615 }),
616 .register_pair_overflow => |pl| try w.print("{t}:({t}:{t})", .{
617 pl.eflags,
618 pl.pair[0],
619 pl.pair[1],
620 }),
607621 .register_mask => |pl| try w.print("mask({s},{f}):{s}{s}", .{
608622 @tagName(pl.info.kind),
609623 pl.info.scalar,
......@@ -693,6 +707,7 @@ const InstTracking = struct {
693707 .register_quadruple,
694708 .register_offset,
695709 .register_overflow,
710 .register_pair_overflow,
696711 .register_mask,
697712 .indirect,
698713 .indirect_mask,
......@@ -750,6 +765,10 @@ const InstTracking = struct {
750765 function.register_manager.freeReg(reg_ov.reg);
751766 function.eflags_inst = null;
752767 },
768 .register_pair_overflow => |rpo| {
769 for (rpo.pair) |r| function.register_manager.freeReg(r);
770 function.eflags_inst = null;
771 },
753772 .register_mask => |reg_mask| function.register_manager.freeReg(reg_mask.reg),
754773 .eflags => function.eflags_inst = null,
755774 else => {}, // TODO process stack allocation death
......@@ -793,6 +812,7 @@ const InstTracking = struct {
793812 .register_quadruple,
794813 .register_offset,
795814 .register_overflow,
815 .register_pair_overflow,
796816 .register_mask,
797817 .indirect,
798818 .indirect_mask,
......@@ -53047,7 +53067,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
5304753067 }),
5304853068 else => |e| return e,
5304953069 };
53050 try res[0].withOverflow(&res[1], cg);
53070 try res[0].withOverflow(&res[1], ty_pl.ty, cg);
5305153071 try res[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg);
5305253072 },
5305353073 .sub_with_overflow => |air_tag| {
......@@ -53952,7 +53972,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
5395253972 }),
5395353973 else => |e| return e,
5395453974 };
53955 try res[0].withOverflow(&res[1], cg);
53975 try res[0].withOverflow(&res[1], ty_pl.ty, cg);
5395653976 try res[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg);
5395753977 },
5395853978 .mul_with_overflow => |air_tag| {
......@@ -57549,7 +57569,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
5754957569 }),
5755057570 else => |e| return e,
5755157571 };
57552 try res[0].withOverflow(&res[1], cg);
57572 try res[0].withOverflow(&res[1], ty_pl.ty, cg);
5755357573 try res[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg);
5755457574 },
5755557575 .shl_with_overflow => |air_tag| {
......@@ -60894,7 +60914,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
6089460914 }),
6089560915 else => |e| return e,
6089660916 };
60897 try res[0].withOverflow(&res[1], cg);
60917 try res[0].withOverflow(&res[1], ty_pl.ty, cg);
6089860918 try res[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg);
6089960919 },
6090060920 .alloc => {
......@@ -104070,7 +104090,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
104070104090 const ty_pl = air_datas[@backingInt(inst)].ty_pl;
104071104091 const bin_op = cg.air.extraData(Air.Bin, ty_pl.payload).data;
104072104092 var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs });
104073 try ops[0].toPair(&ops[1], cg);
104093 try ops[0].toPair(&ops[1], ty_pl.ty, cg);
104074104094 try ops[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg);
104075104095 },
104076104096 .slice_len => {
......@@ -104487,7 +104507,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
104487104507 var len = try cg.tempInit(.usize, .{
104488104508 .immediate = cg.typeOf(ty_op.operand).childType(zcu).arrayLen(zcu),
104489104509 });
104490 try ops[0].toPair(&len, cg);
104510 try ops[0].toPair(&len, ty_op.ty, cg);
104491104511 try ops[0].finish(inst, &.{ty_op.operand}, &ops, cg);
104492104512 },
104493104513 .int_from_float, .int_from_float_optimized => |air_tag| {
......@@ -171079,7 +171099,113 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
171079171099 };
171080171100 for (ops) |op| try op.die(cg);
171081171101 },
171082 .cmpxchg_weak, .cmpxchg_strong => try cg.airCmpxchg(inst),
171102 .cmpxchg_weak, .cmpxchg_strong => |air_tag| {
171103 const ty_pl = air_datas[@backingInt(inst)].ty_pl;
171104 const extra = cg.air.extraData(Air.Cmpxchg, ty_pl.payload).data;
171105 var ops = try cg.tempsFromOperands(inst, .{ extra.ptr, extra.expected_value, extra.new_value });
171106 var res: [2]Temp = undefined;
171107 (if (ty_pl.ty.optionalReprIsPayload(zcu))
171108 cg.select(res[0..1], &.{ty_pl.ty}, &ops, comptime &.{.{
171109 .required_features = .{ .@"64bit", .cmov, null, null },
171110 .src_constraints = .{ .any, .{ .size = .qword }, .{ .size = .qword } },
171111 .patterns = &.{
171112 .{ .src = .{ .to_gpr, .{ .to_reg = .rax }, .to_gpr } },
171113 },
171114 .extra_temps = .{
171115 .{ .type = .usize, .kind = .{ .rc = .general_purpose } },
171116 .unused,
171117 .unused,
171118 .unused,
171119 .unused,
171120 .unused,
171121 .unused,
171122 .unused,
171123 .unused,
171124 .unused,
171125 .unused,
171126 },
171127 .dst_temps = .{ .{ .ref = .src1 }, .unused },
171128 .clobbers = .{ .eflags = true },
171129 .each = .{ .once = &.{
171130 .{ ._, ._, .xor, .tmp0d, .tmp0d, ._, ._ },
171131 .{ ._, .@"lock _", .cmpxchg, .lea(.src0q), .src2q, ._, ._ },
171132 .{ ._, ._e, .cmov, .dst0q, .tmp0q, ._, ._ },
171133 } },
171134 }})
171135 else
171136 cg.select(&res, &.{ ty_pl.ty.childType(zcu), .u1 }, &ops, comptime &.{ .{
171137 .required_features = .{ .@"64bit", null, null, null },
171138 .src_constraints = .{ .any, .{ .int = .byte }, .{ .int = .byte } },
171139 .patterns = &.{
171140 .{ .src = .{ .to_gpr, .{ .to_reg = .rax }, .to_gpr } },
171141 },
171142 .dst_temps = .{ .{ .ref = .src1 }, .{ .cc = .ne } },
171143 .clobbers = .{ .eflags = true },
171144 .each = .{ .once = &.{
171145 .{ ._, .@"lock _", .cmpxchg, .lea(.src0b), .src2b, ._, ._ },
171146 } },
171147 }, .{
171148 .required_features = .{ .@"64bit", null, null, null },
171149 .src_constraints = .{ .any, .{ .int = .word }, .{ .int = .word } },
171150 .patterns = &.{
171151 .{ .src = .{ .to_gpr, .{ .to_reg = .rax }, .to_gpr } },
171152 },
171153 .dst_temps = .{ .{ .ref = .src1 }, .{ .cc = .ne } },
171154 .clobbers = .{ .eflags = true },
171155 .each = .{ .once = &.{
171156 .{ ._, .@"lock _", .cmpxchg, .lea(.src0w), .src2w, ._, ._ },
171157 } },
171158 }, .{
171159 .required_features = .{ .@"64bit", null, null, null },
171160 .src_constraints = .{ .any, .{ .int = .dword }, .{ .int = .dword } },
171161 .patterns = &.{
171162 .{ .src = .{ .to_gpr, .{ .to_reg = .rax }, .to_gpr } },
171163 },
171164 .dst_temps = .{ .{ .ref = .src1 }, .{ .cc = .ne } },
171165 .clobbers = .{ .eflags = true },
171166 .each = .{ .once = &.{
171167 .{ ._, .@"lock _", .cmpxchg, .lea(.src0d), .src2d, ._, ._ },
171168 } },
171169 }, .{
171170 .required_features = .{ .@"64bit", null, null, null },
171171 .src_constraints = .{ .any, .{ .int = .qword }, .{ .int = .qword } },
171172 .patterns = &.{
171173 .{ .src = .{ .to_gpr, .{ .to_reg = .rax }, .to_gpr } },
171174 },
171175 .dst_temps = .{ .{ .ref = .src1 }, .{ .cc = .ne } },
171176 .clobbers = .{ .eflags = true },
171177 .each = .{ .once = &.{
171178 .{ ._, .@"lock _", .cmpxchg, .lea(.src0q), .src2q, ._, ._ },
171179 } },
171180 }, .{
171181 .required_features = .{ .@"64bit", .cx16, null, null },
171182 .src_constraints = .{ .any, .{ .int = .xword }, .{ .int = .xword } },
171183 .patterns = &.{.{
171184 .src = .{
171185 .to_gpr,
171186 .{ .to_reg_pair = .{ .rax, .rdx } },
171187 .{ .to_reg_pair = .{ .rbx, .rcx } },
171188 },
171189 }},
171190 .dst_temps = .{ .{ .ref = .src1 }, .{ .cc = .ne } },
171191 .clobbers = .{ .eflags = true },
171192 .each = .{ .once = &.{
171193 .{ ._, .@"lock _16b", .cmpxchg, .lea(.src0x), ._, ._, ._ },
171194 } },
171195 } })) catch |err| switch (err) {
171196 error.SelectFailed => return cg.fail("failed to select {s} {f} {f} {f} {f}", .{
171197 @tagName(air_tag),
171198 ty_pl.ty.fmt(zcu),
171199 ops[0].tracking(cg),
171200 ops[1].tracking(cg),
171201 ops[2].tracking(cg),
171202 }),
171203 else => |e| return e,
171204 };
171205 if (!ty_pl.ty.optionalReprIsPayload(zcu))
171206 try res[0].withOverflow(&res[1], ty_pl.ty, cg);
171207 try res[0].finish(inst, &.{ extra.ptr, extra.expected_value, extra.new_value }, &ops, cg);
171208 },
171083171209 .atomic_load => try cg.airAtomicLoad(inst),
171084171210 .atomic_store_unordered => try cg.airAtomicStore(inst, .unordered),
171085171211 .atomic_store_monotonic => try cg.airAtomicStore(inst, .monotonic),
......@@ -171421,7 +171547,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
171421171547 }),
171422171548 else => |e| return e,
171423171549 };
171424 if (res[0].tracking(cg).short != .register_pair) try ops[0].toPair(&res[0], cg);
171550 if (res[0].tracking(cg).short != .register_pair) try ops[0].toPair(&res[0], .slice_const_u8_sentinel_0, cg);
171425171551 try res[0].finish(inst, &.{un_op}, &ops, cg);
171426171552 },
171427171553 .error_name => |air_tag| {
......@@ -171524,7 +171650,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
171524171650 else => |e| return e,
171525171651 };
171526171652 const orig_res = res;
171527 try res[0].toPair(&res[1], cg);
171653 try res[0].toPair(&res[1], .slice_const_u8_sentinel_0, cg);
171528171654 for (&ops) |*op| for (orig_res) |orig_r| {
171529171655 if (op.index != orig_r.index) continue;
171530171656 op.* = res[0];
......@@ -174108,7 +174234,10 @@ fn getValue(self: *CodeGen, value: MCValue, inst: ?Air.Inst.Index) !void {
174108174234 for (value.getRegs()) |reg| try self.register_manager.getReg(reg, inst);
174109174235 switch (value) {
174110174236 else => {},
174111 .eflags, .register_overflow => self.eflags_inst = inst,
174237 .eflags,
174238 .register_overflow,
174239 .register_pair_overflow,
174240 => self.eflags_inst = inst,
174112174241 }
174113174242}
174114174243
......@@ -174136,6 +174265,10 @@ fn freeValue(self: *CodeGen, value: MCValue, comptime opts: FreeOptions) !void {
174136174265 try self.freeReg(reg_ov.reg, opts);
174137174266 self.eflags_inst = null;
174138174267 },
174268 .register_pair_overflow => |rpo| {
174269 for (rpo.pair) |r| try self.freeReg(r, opts);
174270 self.eflags_inst = null;
174271 },
174139174272 .register_mask => |reg_mask| try self.freeReg(reg_mask.reg, opts),
174140174273 .eflags => self.eflags_inst = null,
174141174274 else => {}, // TODO process stack allocation death
......@@ -174678,7 +174811,12 @@ fn reuseOperandAdvanced(
174678174811 return false;
174679174812
174680174813 switch (mcv) {
174681 .register, .register_pair, .register_overflow, .register_mask => for (mcv.getRegs()) |reg| {
174814 .register,
174815 .register_pair,
174816 .register_overflow,
174817 .register_pair_overflow,
174818 .register_mask,
174819 => for (mcv.getRegs()) |reg| {
174682174820 // If it's in the registers table, need to associate the register(s) with the
174683174821 // new instruction.
174684174822 if (maybe_tracked_inst) |tracked_inst| {
......@@ -174693,7 +174831,10 @@ fn reuseOperandAdvanced(
174693174831 else => return false,
174694174832 }
174695174833 switch (mcv) {
174696 .eflags, .register_overflow => self.eflags_inst = maybe_tracked_inst,
174834 .eflags,
174835 .register_overflow,
174836 .register_pair_overflow,
174837 => self.eflags_inst = maybe_tracked_inst,
174697174838 else => {},
174698174839 }
174699174840
......@@ -174720,6 +174861,7 @@ fn load(self: *CodeGen, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerE
174720174861 .register_triple,
174721174862 .register_quadruple,
174722174863 .register_overflow,
174864 .register_pair_overflow,
174723174865 .register_mask,
174724174866 .indirect_load_frame,
174725174867 .indirect_mask,
......@@ -174779,6 +174921,7 @@ fn store(
174779174921 .register_triple,
174780174922 .register_quadruple,
174781174923 .register_overflow,
174924 .register_pair_overflow,
174782174925 .register_mask,
174783174926 .indirect_load_frame,
174784174927 .indirect_mask,
......@@ -174830,6 +174973,7 @@ fn genUnOpMir(self: *CodeGen, mir_tag: Mir.Inst.FixedTag, dst_ty: Type, dst_mcv:
174830174973 .register_offset,
174831174974 .eflags,
174832174975 .register_overflow,
174976 .register_pair_overflow,
174833174977 .register_mask,
174834174978 .indirect_load_frame,
174835174979 .indirect_mask,
......@@ -175526,6 +175670,7 @@ fn genBinOpMir(
175526175670 .immediate,
175527175671 .eflags,
175528175672 .register_overflow,
175673 .register_pair_overflow,
175529175674 .register_mask,
175530175675 .lea_frame,
175531175676 .lea_nav,
......@@ -175574,6 +175719,7 @@ fn genBinOpMir(
175574175719 .dead,
175575175720 .undef,
175576175721 .register_overflow,
175722 .register_pair_overflow,
175577175723 .register_mask,
175578175724 .indirect_load_frame,
175579175725 .indirect_mask,
......@@ -175752,6 +175898,7 @@ fn genBinOpMir(
175752175898 .dead,
175753175899 .undef,
175754175900 .register_overflow,
175901 .register_pair_overflow,
175755175902 .register_mask,
175756175903 .indirect_load_frame,
175757175904 .indirect_mask,
......@@ -175862,6 +176009,7 @@ fn genBinOpMir(
175862176009 .dead,
175863176010 .undef,
175864176011 .register_overflow,
176012 .register_pair_overflow,
175865176013 .register_mask,
175866176014 .indirect_load_frame,
175867176015 .indirect_mask,
......@@ -178971,6 +179119,7 @@ fn genCopy(self: *CodeGen, ty: Type, dst_mcv: MCValue, src_mcv: MCValue, opts: C
178971179119 .immediate,
178972179120 .eflags,
178973179121 .register_overflow,
179122 .register_pair_overflow,
178974179123 .register_mask,
178975179124 .indirect_load_frame,
178976179125 .indirect_mask,
......@@ -178995,6 +179144,7 @@ fn genCopy(self: *CodeGen, ty: Type, dst_mcv: MCValue, src_mcv: MCValue, opts: C
178995179144 .dead,
178996179145 .undef,
178997179146 .register_overflow,
179147 .register_pair_overflow,
178998179148 .register_tee,
178999179149 .elementwise_gpr,
179000179150 .elementwise_sse,
......@@ -179198,6 +179348,7 @@ fn genSetReg(
179198179348 .ywordwise_sse,
179199179349 .zwordwise_sse,
179200179350 .reserved_frame,
179351 .register_pair_overflow,
179201179352 => unreachable,
179202179353 .undef => if (opts.safety) switch (dst_reg.class()) {
179203179354 .general_purpose, .gphi => switch (abi_size) {
......@@ -180001,6 +180152,23 @@ fn genSetMem(
180001180152 @tagName(src_mcv), ty.fmt(zcu),
180002180153 }),
180003180154 },
180155 .register_pair_overflow => |rpo| switch (ty.zigTypeTag(zcu)) {
180156 .optional => {
180157 assert(!ty.optionalReprIsPayload(zcu));
180158 const child_ty = ty.optionalChild(zcu);
180159 try cg.genSetMem(base, disp, child_ty, .{ .register_pair = rpo.pair }, opts);
180160 try cg.genSetMem(
180161 base,
180162 disp + @as(i32, @intCast(child_ty.abiSize(zcu))),
180163 .bool,
180164 .{ .eflags = rpo.eflags },
180165 opts,
180166 );
180167 },
180168 else => return cg.fail("TODO implement genSetMem for {s} of {f}", .{
180169 @tagName(src_mcv), ty.fmt(zcu),
180170 }),
180171 },
180004180172 .register_offset => |reg_off| {
180005180173 const src_reg = cg.copyToTmpRegister(ty, src_mcv) catch |err| switch (err) {
180006180174 error.OutOfRegisters => {
......@@ -180335,111 +180503,6 @@ fn airBitCast(self: *CodeGen, inst: Air.Inst.Index) !void {
180335180503 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
180336180504}
180337180505
180338fn airCmpxchg(self: *CodeGen, inst: Air.Inst.Index) !void {
180339 const pt = self.pt;
180340 const zcu = pt.zcu;
180341 const ty_pl = self.air.instructions.items(.data)[@backingInt(inst)].ty_pl;
180342 const extra = self.air.extraData(Air.Cmpxchg, ty_pl.payload).data;
180343
180344 const dst_ty = self.typeOfIndex(inst);
180345 const ptr_ty = self.typeOf(extra.ptr);
180346 const val_ty = self.typeOf(extra.expected_value);
180347 const val_abi_size: u32 = @intCast(val_ty.abiSize(zcu));
180348
180349 try self.spillRegisters(&.{ .rax, .rdx, .rbx, .rcx });
180350 const regs_lock = self.register_manager.lockRegsAssumeUnused(4, .{ .rax, .rdx, .rbx, .rcx });
180351 defer for (regs_lock) |lock| self.register_manager.unlockReg(lock);
180352
180353 const exp_mcv = try self.resolveInst(extra.expected_value);
180354 if (val_abi_size > 8) {
180355 const exp_addr_mcv: MCValue = switch (exp_mcv) {
180356 .memory, .indirect, .load_frame => exp_mcv.address(),
180357 else => .{ .register = try self.copyToTmpRegister(.usize, exp_mcv.address()) },
180358 };
180359 const exp_addr_lock =
180360 if (exp_addr_mcv.getReg()) |reg| self.register_manager.lockReg(reg) else null;
180361 defer if (exp_addr_lock) |lock| self.register_manager.unlockReg(lock);
180362
180363 try self.genSetReg(.rax, .usize, exp_addr_mcv.deref(), .{});
180364 try self.genSetReg(.rdx, .usize, exp_addr_mcv.offset(8).deref(), .{});
180365 } else try self.genSetReg(.rax, val_ty, exp_mcv, .{});
180366
180367 const new_mcv = try self.resolveInst(extra.new_value);
180368 const new_reg = if (val_abi_size > 8) new: {
180369 const new_addr_mcv: MCValue = switch (new_mcv) {
180370 .memory, .indirect, .load_frame => new_mcv.address(),
180371 else => .{ .register = try self.copyToTmpRegister(.usize, new_mcv.address()) },
180372 };
180373 const new_addr_lock =
180374 if (new_addr_mcv.getReg()) |reg| self.register_manager.lockReg(reg) else null;
180375 defer if (new_addr_lock) |lock| self.register_manager.unlockReg(lock);
180376
180377 try self.genSetReg(.rbx, .usize, new_addr_mcv.deref(), .{});
180378 try self.genSetReg(.rcx, .usize, new_addr_mcv.offset(8).deref(), .{});
180379 break :new null;
180380 } else try self.copyToTmpRegister(val_ty, new_mcv);
180381 const new_lock = if (new_reg) |reg| self.register_manager.lockRegAssumeUnused(reg) else null;
180382 defer if (new_lock) |lock| self.register_manager.unlockReg(lock);
180383
180384 const ptr_mcv = try self.resolveInst(extra.ptr);
180385 const mem_size: Memory.Size = .fromSize(val_abi_size);
180386 const ptr_mem: Memory = switch (ptr_mcv) {
180387 .immediate, .register, .register_offset, .lea_frame => try ptr_mcv.deref().mem(self, .{ .size = mem_size }),
180388 else => .{
180389 .base = .{ .reg = try self.copyToTmpRegister(ptr_ty, ptr_mcv) },
180390 .mod = .{ .rm = .{ .size = mem_size } },
180391 },
180392 };
180393 switch (ptr_mem.mod) {
180394 .rm => {},
180395 .off => return self.fail("TODO airCmpxchg with {s}", .{@tagName(ptr_mcv)}),
180396 }
180397 const ptr_lock = switch (ptr_mem.base) {
180398 .none, .frame, .nav, .uav => null,
180399 .reg => |reg| self.register_manager.lockReg(reg),
180400 .table, .rip_inst, .lazy_sym, .extern_func => unreachable,
180401 };
180402 defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock);
180403
180404 try self.spillEflagsIfOccupied();
180405 const null_reg = if (dst_ty.optionalReprIsPayload(zcu) and !self.liveness.isUnused(inst)) null_reg: {
180406 const null_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
180407 try self.asmRegisterRegister(.{ ._, .xor }, null_reg.to32(), null_reg.to32());
180408 break :null_reg null_reg;
180409 } else null;
180410 const null_lock = if (null_reg) |reg| self.register_manager.lockRegAssumeUnused(reg) else null;
180411 defer if (null_lock) |lock| self.register_manager.unlockReg(lock);
180412
180413 if (val_abi_size <= 8) try self.asmMemoryRegister(
180414 .{ .@"lock _", .cmpxchg },
180415 ptr_mem,
180416 registerAlias(new_reg.?, val_abi_size),
180417 ) else try self.asmMemory(.{ .@"lock _16b", .cmpxchg }, ptr_mem);
180418
180419 const result: MCValue = result: {
180420 if (self.liveness.isUnused(inst)) break :result .unreach;
180421
180422 if (null_reg) |reg| try self.asmCmovccRegisterRegister(
180423 .e,
180424 registerAlias(.rax, val_abi_size),
180425 registerAlias(reg, val_abi_size),
180426 );
180427
180428 if (val_abi_size <= 8) {
180429 if (null_reg) |_| break :result .{ .register = .rax };
180430 self.eflags_inst = inst;
180431 break :result .{ .register_overflow = .{ .reg = .rax, .eflags = .ne } };
180432 }
180433
180434 const dst_mcv = try self.allocRegOrMem(inst, false);
180435 try self.genCopy(.usize, dst_mcv, .{ .register = .rax }, .{});
180436 try self.genCopy(.usize, dst_mcv.address().offset(8).deref(), .{ .register = .rdx }, .{});
180437 if (null_reg == null) try self.genCopy(.bool, dst_mcv.address().offset(16).deref(), .{ .eflags = .ne }, .{});
180438 break :result dst_mcv;
180439 };
180440 return self.finishAir(inst, result, .{ extra.ptr, extra.expected_value, extra.new_value });
180441}
180442
180443180506fn atomicOp(
180444180507 self: *CodeGen,
180445180508 ptr_mcv: MCValue,
......@@ -180475,7 +180538,7 @@ fn atomicOp(
180475180538 };
180476180539 switch (ptr_mem.mod) {
180477180540 .rm => {},
180478 .off => return self.fail("TODO airCmpxchg with {s}", .{@tagName(ptr_mcv)}),
180541 .off => return self.fail("TODO atomicOp with {s}", .{@tagName(ptr_mcv)}),
180479180542 }
180480180543 const mem_lock = switch (ptr_mem.base) {
180481180544 .none, .frame, .nav, .uav => null,
......@@ -182855,6 +182918,7 @@ const Temp = struct {
182855182918 .register_triple,
182856182919 .register_quadruple,
182857182920 .register_overflow,
182921 .register_pair_overflow,
182858182922 => true,
182859182923 .load_frame => |frame_addr| !frame_addr.index.isNamed(),
182860182924 },
......@@ -183235,7 +183299,7 @@ const Temp = struct {
183235183299 return true;
183236183300 }
183237183301
183238 fn toPair(first_temp: *Temp, second_temp: *Temp, cg: *CodeGen) InnerError!void {
183302 fn toPair(first_temp: *Temp, second_temp: *Temp, result_ty: Type, cg: *CodeGen) InnerError!void {
183239183303 while (true) for ([_]*Temp{ first_temp, second_temp }) |part_temp| {
183240183304 if (try part_temp.toRegClass(true, .general_purpose, cg)) break;
183241183305 } else break;
......@@ -183250,13 +183314,13 @@ const Temp = struct {
183250183314 assert(cg.reuseTemp(result_temp.index, first_temp.index, first_temp_tracking));
183251183315 assert(cg.reuseTemp(result_temp.index, second_temp.index, second_temp_tracking));
183252183316 result_temp_index.tracking(cg).* = .init(result);
183253 cg.temp_type[@backingInt(result_temp_index)] = .slice_const_u8;
183317 cg.temp_type[@backingInt(result_temp_index)] = result_ty;
183254183318 cg.next_temp_index = @fromBackingInt(@intCast(@backingInt(result_temp_index) + 1));
183255183319 first_temp.* = result_temp;
183256183320 second_temp.* = result_temp;
183257183321 }
183258183322
183259 fn withOverflow(temp: *Temp, overflow_temp: *Temp, cg: *CodeGen) InnerError!void {
183323 fn withOverflow(temp: *Temp, overflow_temp: *Temp, result_ty: Type, cg: *CodeGen) InnerError!void {
183260183324 const temp_index = temp.unwrap(cg).temp;
183261183325 const temp_tracking = temp_index.tracking(cg);
183262183326 const overflow_temp_tracking = overflow_temp.unwrap(cg).temp.tracking(cg);
......@@ -183268,7 +183332,7 @@ const Temp = struct {
183268183332 assert(cg.reuseTemp(result_temp.index, temp.index, temp_tracking));
183269183333 assert(cg.reuseTemp(result_temp.index, overflow_temp.index, overflow_temp_tracking));
183270183334 result_temp_index.tracking(cg).* = .init(.{ .register_overflow = .{ .reg = reg, .eflags = overflow_cc } });
183271 cg.temp_type[@backingInt(result_temp_index)] = .slice_const_u8;
183335 cg.temp_type[@backingInt(result_temp_index)] = result_ty;
183272183336 cg.next_temp_index = @fromBackingInt(@intCast(@backingInt(result_temp_index) + 1));
183273183337 temp.* = result_temp;
183274183338 overflow_temp.* = result_temp;
......@@ -183280,7 +183344,22 @@ const Temp = struct {
183280183344 assert(cg.reuseTemp(result_temp.index, temp.index, temp_tracking));
183281183345 assert(cg.reuseTemp(result_temp.index, overflow_temp.index, overflow_temp_tracking));
183282183346 result_temp_index.tracking(cg).* = .init(.{ .register_pair = .{ reg, overflow_reg } });
183283 cg.temp_type[@backingInt(result_temp_index)] = .slice_const_u8;
183347 cg.temp_type[@backingInt(result_temp_index)] = result_ty;
183348 cg.next_temp_index = @fromBackingInt(@intCast(@backingInt(result_temp_index) + 1));
183349 temp.* = result_temp;
183350 overflow_temp.* = result_temp;
183351 return;
183352 },
183353 else => {},
183354 },
183355 .register_pair => |pair| switch (overflow_temp_tracking.short) {
183356 .eflags => |overflow_cc| {
183357 const result_temp_index = cg.next_temp_index;
183358 const result_temp: Temp = .{ .index = result_temp_index.toIndex() };
183359 assert(cg.reuseTemp(result_temp.index, temp.index, temp_tracking));
183360 assert(cg.reuseTemp(result_temp.index, overflow_temp.index, overflow_temp_tracking));
183361 result_temp_index.tracking(cg).* = .init(.{ .register_pair_overflow = .{ .pair = pair, .eflags = overflow_cc } });
183362 cg.temp_type[@backingInt(result_temp_index)] = result_ty;
183284183363 cg.next_temp_index = @fromBackingInt(@intCast(@backingInt(result_temp_index) + 1));
183285183364 temp.* = result_temp;
183286183365 overflow_temp.* = result_temp;
......@@ -183320,6 +183399,7 @@ const Temp = struct {
183320183399 .register_triple,
183321183400 .register_quadruple,
183322183401 .register_overflow,
183402 .register_pair_overflow,
183323183403 .register_mask,
183324183404 .indirect_load_frame,
183325183405 .indirect_mask,
......@@ -189802,6 +189882,7 @@ const Temp = struct {
189802189882 .register_quadruple,
189803189883 .register_offset,
189804189884 .register_overflow,
189885 .register_pair_overflow,
189805189886 .register_mask,
189806189887 .memory,
189807189888 .indirect,
......@@ -189920,6 +190001,7 @@ fn reuseTemp(
189920190001 .register_pair,
189921190002 .register_offset,
189922190003 .register_overflow,
190004 .register_pair_overflow,
189923190005 .register_mask,
189924190006 .indirect,
189925190007 => for (tracking.short.getRegs()) |tracked_reg| {
......@@ -189931,7 +190013,10 @@ fn reuseTemp(
189931190013 else => {},
189932190014 }
189933190015 switch (tracking.short) {
189934 .eflags, .register_overflow => cg.eflags_inst = new_inst,
190016 .eflags,
190017 .register_overflow,
190018 .register_pair_overflow,
190019 => cg.eflags_inst = new_inst,
189935190020 else => {},
189936190021 }
189937190022 tracking.reuse(cg, new_inst, old_inst);
......@@ -192514,7 +192599,13 @@ const Select = struct {
192514192599 } },
192515192600 .eflags => unreachable,
192516192601 .register => |reg| .{ .reg = s.lowerReg(reg.toSize(op.flags.base.size, s.cg.target)) },
192517 .register_pair, .register_triple, .register_quadruple, .register_offset, .register_overflow => unreachable,
192602 .register_pair,
192603 .register_triple,
192604 .register_quadruple,
192605 .register_offset,
192606 .register_overflow,
192607 .register_pair_overflow,
192608 => unreachable,
192518192609 .register_mask => |reg_mask| .{ .reg = s.lowerReg(reg_mask.reg.toSize(op.flags.base.size, s.cg.target)) },
192519192610 .lea_nav => |nav| .{ .imm = .{ .nav = .{ .index = nav } } },
192520192611 .lea_uav => |uav| .{ .imm = .{ .uav = uav } },
test/behavior/atomics.zig+7
......@@ -133,6 +133,13 @@ fn test_u128_cmpxchg() !void {
133133
134134 try expect(@cmpxchgStrong(u128, &x, 5678, 42, .seq_cst, .seq_cst) == null);
135135 try expect(x == 42);
136
137 var a: u128 align(16) = 40;
138 var b: u128 align(16) = 2;
139 _ = &a;
140 _ = &b;
141 try expect(@cmpxchgStrong(u128, &x, a | b, a & b, .seq_cst, .seq_cst) == null);
142 try expect(x == 0);
136143}
137144
138145var a_global_variable = @as(u32, 1234);