authorgravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2026-10-23 17:04:46-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-10-24 22:59:09+02:00
log16126cf14165bf97ed3c588fbc10f13c1970f22f
treec93db09058e4d0c1a06d3d92a5694a8d0ee6d115
parentdef373ed782112e19132be11b413a6f44608bc60

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) {...@@ -210,6 +210,8 @@ pub const MCValue = union(enum) {
210 register_offset: bits.RegisterOffset,210 register_offset: bits.RegisterOffset,
211 /// The value is a tuple { wrapped, overflow } where wrapped value is stored in the GP register.211 /// The value is a tuple { wrapped, overflow } where wrapped value is stored in the GP register.
212 register_overflow: struct { reg: Register, eflags: Condition },212 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 },
213 /// The value is a bool vector stored in a vector register with a different scalar type.215 /// The value is a bool vector stored in a vector register with a different scalar type.
214 register_mask: Mask,216 register_mask: Mask,
215 /// The value is in memory at a hard-coded address.217 /// The value is in memory at a hard-coded address.
...@@ -265,6 +267,7 @@ pub const MCValue = union(enum) {...@@ -265,6 +267,7 @@ pub const MCValue = union(enum) {
265 .register_mask,267 .register_mask,
266 .eflags,268 .eflags,
267 .register_overflow,269 .register_overflow,
270 .register_pair_overflow,
268 .indirect_load_frame,271 .indirect_load_frame,
269 .lea_frame,272 .lea_frame,
270 .lea_nav,273 .lea_nav,
...@@ -363,6 +366,7 @@ pub const MCValue = union(enum) {...@@ -363,6 +366,7 @@ pub const MCValue = union(enum) {
363 .indirect,366 .indirect,
364 .indirect_mask,367 .indirect_mask,
365 => |*pl| (&pl.reg)[0..1],368 => |*pl| (&pl.reg)[0..1],
369 .register_pair_overflow => |*rpo| &rpo.pair,
366 else => &.{},370 else => &.{},
367 };371 };
368 }372 }
...@@ -371,6 +375,7 @@ pub const MCValue = union(enum) {...@@ -371,6 +375,7 @@ pub const MCValue = union(enum) {
371 return switch (mcv) {375 return switch (mcv) {
372 .eflags => |cc| cc,376 .eflags => |cc| cc,
373 .register_overflow => |reg_ov| reg_ov.eflags,377 .register_overflow => |reg_ov| reg_ov.eflags,
378 .register_pair_overflow => |rpo| rpo.eflags,
374 else => null,379 else => null,
375 };380 };
376 }381 }
...@@ -396,6 +401,7 @@ pub const MCValue = union(enum) {...@@ -396,6 +401,7 @@ pub const MCValue = union(enum) {
396 .register_quadruple,401 .register_quadruple,
397 .register_offset,402 .register_offset,
398 .register_overflow,403 .register_overflow,
404 .register_pair_overflow,
399 .register_mask,405 .register_mask,
400 .indirect_mask,406 .indirect_mask,
401 .lea_frame,407 .lea_frame,
...@@ -437,6 +443,7 @@ pub const MCValue = union(enum) {...@@ -437,6 +443,7 @@ pub const MCValue = union(enum) {
437 .register_triple,443 .register_triple,
438 .register_quadruple,444 .register_quadruple,
439 .register_overflow,445 .register_overflow,
446 .register_pair_overflow,
440 .register_mask,447 .register_mask,
441 .memory,448 .memory,
442 .indirect,449 .indirect,
...@@ -487,6 +494,7 @@ pub const MCValue = union(enum) {...@@ -487,6 +494,7 @@ pub const MCValue = union(enum) {
487 .register_triple,494 .register_triple,
488 .register_quadruple,495 .register_quadruple,
489 .register_overflow,496 .register_overflow,
497 .register_pair_overflow,
490 .register_mask,498 .register_mask,
491 .memory,499 .memory,
492 .indirect,500 .indirect,
...@@ -530,6 +538,7 @@ pub const MCValue = union(enum) {...@@ -530,6 +538,7 @@ pub const MCValue = union(enum) {
530 .register_quadruple,538 .register_quadruple,
531 .register_offset,539 .register_offset,
532 .register_overflow,540 .register_overflow,
541 .register_pair_overflow,
533 .register_mask,542 .register_mask,
534 .indirect_load_frame,543 .indirect_load_frame,
535 .lea_frame,544 .lea_frame,
...@@ -604,6 +613,11 @@ pub const MCValue = union(enum) {...@@ -604,6 +613,11 @@ pub const MCValue = union(enum) {
604 @tagName(pl.eflags),613 @tagName(pl.eflags),
605 @tagName(pl.reg),614 @tagName(pl.reg),
606 }),615 }),
616 .register_pair_overflow => |pl| try w.print("{t}:({t}:{t})", .{
617 pl.eflags,
618 pl.pair[0],
619 pl.pair[1],
620 }),
607 .register_mask => |pl| try w.print("mask({s},{f}):{s}{s}", .{621 .register_mask => |pl| try w.print("mask({s},{f}):{s}{s}", .{
608 @tagName(pl.info.kind),622 @tagName(pl.info.kind),
609 pl.info.scalar,623 pl.info.scalar,
...@@ -693,6 +707,7 @@ const InstTracking = struct {...@@ -693,6 +707,7 @@ const InstTracking = struct {
693 .register_quadruple,707 .register_quadruple,
694 .register_offset,708 .register_offset,
695 .register_overflow,709 .register_overflow,
710 .register_pair_overflow,
696 .register_mask,711 .register_mask,
697 .indirect,712 .indirect,
698 .indirect_mask,713 .indirect_mask,
...@@ -750,6 +765,10 @@ const InstTracking = struct {...@@ -750,6 +765,10 @@ const InstTracking = struct {
750 function.register_manager.freeReg(reg_ov.reg);765 function.register_manager.freeReg(reg_ov.reg);
751 function.eflags_inst = null;766 function.eflags_inst = null;
752 },767 },
768 .register_pair_overflow => |rpo| {
769 for (rpo.pair) |r| function.register_manager.freeReg(r);
770 function.eflags_inst = null;
771 },
753 .register_mask => |reg_mask| function.register_manager.freeReg(reg_mask.reg),772 .register_mask => |reg_mask| function.register_manager.freeReg(reg_mask.reg),
754 .eflags => function.eflags_inst = null,773 .eflags => function.eflags_inst = null,
755 else => {}, // TODO process stack allocation death774 else => {}, // TODO process stack allocation death
...@@ -793,6 +812,7 @@ const InstTracking = struct {...@@ -793,6 +812,7 @@ const InstTracking = struct {
793 .register_quadruple,812 .register_quadruple,
794 .register_offset,813 .register_offset,
795 .register_overflow,814 .register_overflow,
815 .register_pair_overflow,
796 .register_mask,816 .register_mask,
797 .indirect,817 .indirect,
798 .indirect_mask,818 .indirect_mask,
...@@ -51941,7 +51961,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -51941,7 +51961,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
51941 }),51961 }),
51942 else => |e| return e,51962 else => |e| return e,
51943 };51963 };
51944 try res[0].withOverflow(&res[1], cg);51964 try res[0].withOverflow(&res[1], ty_pl.ty, cg);
51945 try res[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg);51965 try res[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg);
51946 },51966 },
51947 .sub_with_overflow => |air_tag| {51967 .sub_with_overflow => |air_tag| {
...@@ -52846,7 +52866,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -52846,7 +52866,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
52846 }),52866 }),
52847 else => |e| return e,52867 else => |e| return e,
52848 };52868 };
52849 try res[0].withOverflow(&res[1], cg);52869 try res[0].withOverflow(&res[1], ty_pl.ty, cg);
52850 try res[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg);52870 try res[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg);
52851 },52871 },
52852 .mul_with_overflow => |air_tag| {52872 .mul_with_overflow => |air_tag| {
...@@ -56443,7 +56463,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -56443,7 +56463,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
56443 }),56463 }),
56444 else => |e| return e,56464 else => |e| return e,
56445 };56465 };
56446 try res[0].withOverflow(&res[1], cg);56466 try res[0].withOverflow(&res[1], ty_pl.ty, cg);
56447 try res[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg);56467 try res[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg);
56448 },56468 },
56449 .shl_with_overflow => |air_tag| {56469 .shl_with_overflow => |air_tag| {
...@@ -59788,7 +59808,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -59788,7 +59808,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
59788 }),59808 }),
59789 else => |e| return e,59809 else => |e| return e,
59790 };59810 };
59791 try res[0].withOverflow(&res[1], cg);59811 try res[0].withOverflow(&res[1], ty_pl.ty, cg);
59792 try res[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg);59812 try res[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg);
59793 },59813 },
59794 .alloc => {59814 .alloc => {
...@@ -103057,7 +103077,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -103057,7 +103077,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
103057 const ty_pl = air_datas[@backingInt(inst)].ty_pl;103077 const ty_pl = air_datas[@backingInt(inst)].ty_pl;
103058 const bin_op = cg.air.extraData(Air.Bin, ty_pl.payload).data;103078 const bin_op = cg.air.extraData(Air.Bin, ty_pl.payload).data;
103059 var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs });103079 var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs });
103060 try ops[0].toPair(&ops[1], cg);103080 try ops[0].toPair(&ops[1], ty_pl.ty, cg);
103061 try ops[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg);103081 try ops[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg);
103062 },103082 },
103063 .slice_len => {103083 .slice_len => {
...@@ -103474,7 +103494,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -103474,7 +103494,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
103474 var len = try cg.tempInit(.usize, .{103494 var len = try cg.tempInit(.usize, .{
103475 .immediate = cg.typeOf(ty_op.operand).childType(zcu).arrayLen(zcu),103495 .immediate = cg.typeOf(ty_op.operand).childType(zcu).arrayLen(zcu),
103476 });103496 });
103477 try ops[0].toPair(&len, cg);103497 try ops[0].toPair(&len, ty_op.ty, cg);
103478 try ops[0].finish(inst, &.{ty_op.operand}, &ops, cg);103498 try ops[0].finish(inst, &.{ty_op.operand}, &ops, cg);
103479 },103499 },
103480 .int_from_float, .int_from_float_optimized => |air_tag| {103500 .int_from_float, .int_from_float_optimized => |air_tag| {
...@@ -170236,7 +170256,113 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -170236,7 +170256,113 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
170236 };170256 };
170237 for (ops) |op| try op.die(cg);170257 for (ops) |op| try op.die(cg);
170238 },170258 },
170239 .cmpxchg_weak, .cmpxchg_strong => try cg.airCmpxchg(inst),170259 .cmpxchg_weak, .cmpxchg_strong => |air_tag| {
170260 const ty_pl = air_datas[@backingInt(inst)].ty_pl;
170261 const extra = cg.air.extraData(Air.Cmpxchg, ty_pl.payload).data;
170262 var ops = try cg.tempsFromOperands(inst, .{ extra.ptr, extra.expected_value, extra.new_value });
170263 var res: [2]Temp = undefined;
170264 (if (ty_pl.ty.optionalReprIsPayload(zcu))
170265 cg.select(res[0..1], &.{ty_pl.ty}, &ops, comptime &.{.{
170266 .required_features = .{ .@"64bit", .cmov, null, null },
170267 .src_constraints = .{ .any, .{ .size = .qword }, .{ .size = .qword } },
170268 .patterns = &.{
170269 .{ .src = .{ .to_gpr, .{ .to_reg = .rax }, .to_gpr } },
170270 },
170271 .extra_temps = .{
170272 .{ .type = .usize, .kind = .{ .rc = .general_purpose } },
170273 .unused,
170274 .unused,
170275 .unused,
170276 .unused,
170277 .unused,
170278 .unused,
170279 .unused,
170280 .unused,
170281 .unused,
170282 .unused,
170283 },
170284 .dst_temps = .{ .{ .ref = .src1 }, .unused },
170285 .clobbers = .{ .eflags = true },
170286 .each = .{ .once = &.{
170287 .{ ._, ._, .xor, .tmp0d, .tmp0d, ._, ._ },
170288 .{ ._, .@"lock _", .cmpxchg, .lea(.src0q), .src2q, ._, ._ },
170289 .{ ._, ._e, .cmov, .dst0q, .tmp0q, ._, ._ },
170290 } },
170291 }})
170292 else
170293 cg.select(&res, &.{ ty_pl.ty.childType(zcu), .u1 }, &ops, comptime &.{ .{
170294 .required_features = .{ .@"64bit", null, null, null },
170295 .src_constraints = .{ .any, .{ .int = .byte }, .{ .int = .byte } },
170296 .patterns = &.{
170297 .{ .src = .{ .to_gpr, .{ .to_reg = .rax }, .to_gpr } },
170298 },
170299 .dst_temps = .{ .{ .ref = .src1 }, .{ .cc = .ne } },
170300 .clobbers = .{ .eflags = true },
170301 .each = .{ .once = &.{
170302 .{ ._, .@"lock _", .cmpxchg, .lea(.src0b), .src2b, ._, ._ },
170303 } },
170304 }, .{
170305 .required_features = .{ .@"64bit", null, null, null },
170306 .src_constraints = .{ .any, .{ .int = .word }, .{ .int = .word } },
170307 .patterns = &.{
170308 .{ .src = .{ .to_gpr, .{ .to_reg = .rax }, .to_gpr } },
170309 },
170310 .dst_temps = .{ .{ .ref = .src1 }, .{ .cc = .ne } },
170311 .clobbers = .{ .eflags = true },
170312 .each = .{ .once = &.{
170313 .{ ._, .@"lock _", .cmpxchg, .lea(.src0w), .src2w, ._, ._ },
170314 } },
170315 }, .{
170316 .required_features = .{ .@"64bit", null, null, null },
170317 .src_constraints = .{ .any, .{ .int = .dword }, .{ .int = .dword } },
170318 .patterns = &.{
170319 .{ .src = .{ .to_gpr, .{ .to_reg = .rax }, .to_gpr } },
170320 },
170321 .dst_temps = .{ .{ .ref = .src1 }, .{ .cc = .ne } },
170322 .clobbers = .{ .eflags = true },
170323 .each = .{ .once = &.{
170324 .{ ._, .@"lock _", .cmpxchg, .lea(.src0d), .src2d, ._, ._ },
170325 } },
170326 }, .{
170327 .required_features = .{ .@"64bit", null, null, null },
170328 .src_constraints = .{ .any, .{ .int = .qword }, .{ .int = .qword } },
170329 .patterns = &.{
170330 .{ .src = .{ .to_gpr, .{ .to_reg = .rax }, .to_gpr } },
170331 },
170332 .dst_temps = .{ .{ .ref = .src1 }, .{ .cc = .ne } },
170333 .clobbers = .{ .eflags = true },
170334 .each = .{ .once = &.{
170335 .{ ._, .@"lock _", .cmpxchg, .lea(.src0q), .src2q, ._, ._ },
170336 } },
170337 }, .{
170338 .required_features = .{ .@"64bit", .cx16, null, null },
170339 .src_constraints = .{ .any, .{ .int = .xword }, .{ .int = .xword } },
170340 .patterns = &.{.{
170341 .src = .{
170342 .to_gpr,
170343 .{ .to_reg_pair = .{ .rax, .rdx } },
170344 .{ .to_reg_pair = .{ .rbx, .rcx } },
170345 },
170346 }},
170347 .dst_temps = .{ .{ .ref = .src1 }, .{ .cc = .ne } },
170348 .clobbers = .{ .eflags = true },
170349 .each = .{ .once = &.{
170350 .{ ._, .@"lock _16b", .cmpxchg, .lea(.src0x), ._, ._, ._ },
170351 } },
170352 } })) catch |err| switch (err) {
170353 error.SelectFailed => return cg.fail("failed to select {s} {f} {f} {f} {f}", .{
170354 @tagName(air_tag),
170355 ty_pl.ty.fmt(zcu),
170356 ops[0].tracking(cg),
170357 ops[1].tracking(cg),
170358 ops[2].tracking(cg),
170359 }),
170360 else => |e| return e,
170361 };
170362 if (!ty_pl.ty.optionalReprIsPayload(zcu))
170363 try res[0].withOverflow(&res[1], ty_pl.ty, cg);
170364 try res[0].finish(inst, &.{ extra.ptr, extra.expected_value, extra.new_value }, &ops, cg);
170365 },
170240 .atomic_load => try cg.airAtomicLoad(inst),170366 .atomic_load => try cg.airAtomicLoad(inst),
170241 .atomic_store_unordered => try cg.airAtomicStore(inst, .unordered),170367 .atomic_store_unordered => try cg.airAtomicStore(inst, .unordered),
170242 .atomic_store_monotonic => try cg.airAtomicStore(inst, .monotonic),170368 .atomic_store_monotonic => try cg.airAtomicStore(inst, .monotonic),
...@@ -170578,7 +170704,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -170578,7 +170704,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
170578 }),170704 }),
170579 else => |e| return e,170705 else => |e| return e,
170580 };170706 };
170581 if (res[0].tracking(cg).short != .register_pair) try ops[0].toPair(&res[0], cg);170707 if (res[0].tracking(cg).short != .register_pair) try ops[0].toPair(&res[0], .slice_const_u8_sentinel_0, cg);
170582 try res[0].finish(inst, &.{un_op}, &ops, cg);170708 try res[0].finish(inst, &.{un_op}, &ops, cg);
170583 },170709 },
170584 .error_name => |air_tag| {170710 .error_name => |air_tag| {
...@@ -170681,7 +170807,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -170681,7 +170807,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
170681 else => |e| return e,170807 else => |e| return e,
170682 };170808 };
170683 const orig_res = res;170809 const orig_res = res;
170684 try res[0].toPair(&res[1], cg);170810 try res[0].toPair(&res[1], .slice_const_u8_sentinel_0, cg);
170685 for (&ops) |*op| for (orig_res) |orig_r| {170811 for (&ops) |*op| for (orig_res) |orig_r| {
170686 if (op.index != orig_r.index) continue;170812 if (op.index != orig_r.index) continue;
170687 op.* = res[0];170813 op.* = res[0];
...@@ -173190,7 +173316,10 @@ fn getValue(self: *CodeGen, value: MCValue, inst: ?Air.Inst.Index) !void {...@@ -173190,7 +173316,10 @@ fn getValue(self: *CodeGen, value: MCValue, inst: ?Air.Inst.Index) !void {
173190 for (value.getRegs()) |reg| try self.register_manager.getReg(reg, inst);173316 for (value.getRegs()) |reg| try self.register_manager.getReg(reg, inst);
173191 switch (value) {173317 switch (value) {
173192 else => {},173318 else => {},
173193 .eflags, .register_overflow => self.eflags_inst = inst,173319 .eflags,
173320 .register_overflow,
173321 .register_pair_overflow,
173322 => self.eflags_inst = inst,
173194 }173323 }
173195}173324}
173196173325
...@@ -173218,6 +173347,10 @@ fn freeValue(self: *CodeGen, value: MCValue, comptime opts: FreeOptions) !void {...@@ -173218,6 +173347,10 @@ fn freeValue(self: *CodeGen, value: MCValue, comptime opts: FreeOptions) !void {
173218 try self.freeReg(reg_ov.reg, opts);173347 try self.freeReg(reg_ov.reg, opts);
173219 self.eflags_inst = null;173348 self.eflags_inst = null;
173220 },173349 },
173350 .register_pair_overflow => |rpo| {
173351 for (rpo.pair) |r| try self.freeReg(r, opts);
173352 self.eflags_inst = null;
173353 },
173221 .register_mask => |reg_mask| try self.freeReg(reg_mask.reg, opts),173354 .register_mask => |reg_mask| try self.freeReg(reg_mask.reg, opts),
173222 .eflags => self.eflags_inst = null,173355 .eflags => self.eflags_inst = null,
173223 else => {}, // TODO process stack allocation death173356 else => {}, // TODO process stack allocation death
...@@ -173760,7 +173893,12 @@ fn reuseOperandAdvanced(...@@ -173760,7 +173893,12 @@ fn reuseOperandAdvanced(
173760 return false;173893 return false;
173761173894
173762 switch (mcv) {173895 switch (mcv) {
173763 .register, .register_pair, .register_overflow, .register_mask => for (mcv.getRegs()) |reg| {173896 .register,
173897 .register_pair,
173898 .register_overflow,
173899 .register_pair_overflow,
173900 .register_mask,
173901 => for (mcv.getRegs()) |reg| {
173764 // If it's in the registers table, need to associate the register(s) with the173902 // If it's in the registers table, need to associate the register(s) with the
173765 // new instruction.173903 // new instruction.
173766 if (maybe_tracked_inst) |tracked_inst| {173904 if (maybe_tracked_inst) |tracked_inst| {
...@@ -173775,7 +173913,10 @@ fn reuseOperandAdvanced(...@@ -173775,7 +173913,10 @@ fn reuseOperandAdvanced(
173775 else => return false,173913 else => return false,
173776 }173914 }
173777 switch (mcv) {173915 switch (mcv) {
173778 .eflags, .register_overflow => self.eflags_inst = maybe_tracked_inst,173916 .eflags,
173917 .register_overflow,
173918 .register_pair_overflow,
173919 => self.eflags_inst = maybe_tracked_inst,
173779 else => {},173920 else => {},
173780 }173921 }
173781173922
...@@ -173802,6 +173943,7 @@ fn load(self: *CodeGen, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerE...@@ -173802,6 +173943,7 @@ fn load(self: *CodeGen, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerE
173802 .register_triple,173943 .register_triple,
173803 .register_quadruple,173944 .register_quadruple,
173804 .register_overflow,173945 .register_overflow,
173946 .register_pair_overflow,
173805 .register_mask,173947 .register_mask,
173806 .indirect_load_frame,173948 .indirect_load_frame,
173807 .indirect_mask,173949 .indirect_mask,
...@@ -173861,6 +174003,7 @@ fn store(...@@ -173861,6 +174003,7 @@ fn store(
173861 .register_triple,174003 .register_triple,
173862 .register_quadruple,174004 .register_quadruple,
173863 .register_overflow,174005 .register_overflow,
174006 .register_pair_overflow,
173864 .register_mask,174007 .register_mask,
173865 .indirect_load_frame,174008 .indirect_load_frame,
173866 .indirect_mask,174009 .indirect_mask,
...@@ -173912,6 +174055,7 @@ fn genUnOpMir(self: *CodeGen, mir_tag: Mir.Inst.FixedTag, dst_ty: Type, dst_mcv:...@@ -173912,6 +174055,7 @@ fn genUnOpMir(self: *CodeGen, mir_tag: Mir.Inst.FixedTag, dst_ty: Type, dst_mcv:
173912 .register_offset,174055 .register_offset,
173913 .eflags,174056 .eflags,
173914 .register_overflow,174057 .register_overflow,
174058 .register_pair_overflow,
173915 .register_mask,174059 .register_mask,
173916 .indirect_load_frame,174060 .indirect_load_frame,
173917 .indirect_mask,174061 .indirect_mask,
...@@ -174608,6 +174752,7 @@ fn genBinOpMir(...@@ -174608,6 +174752,7 @@ fn genBinOpMir(
174608 .immediate,174752 .immediate,
174609 .eflags,174753 .eflags,
174610 .register_overflow,174754 .register_overflow,
174755 .register_pair_overflow,
174611 .register_mask,174756 .register_mask,
174612 .lea_frame,174757 .lea_frame,
174613 .lea_nav,174758 .lea_nav,
...@@ -174656,6 +174801,7 @@ fn genBinOpMir(...@@ -174656,6 +174801,7 @@ fn genBinOpMir(
174656 .dead,174801 .dead,
174657 .undef,174802 .undef,
174658 .register_overflow,174803 .register_overflow,
174804 .register_pair_overflow,
174659 .register_mask,174805 .register_mask,
174660 .indirect_load_frame,174806 .indirect_load_frame,
174661 .indirect_mask,174807 .indirect_mask,
...@@ -174834,6 +174980,7 @@ fn genBinOpMir(...@@ -174834,6 +174980,7 @@ fn genBinOpMir(
174834 .dead,174980 .dead,
174835 .undef,174981 .undef,
174836 .register_overflow,174982 .register_overflow,
174983 .register_pair_overflow,
174837 .register_mask,174984 .register_mask,
174838 .indirect_load_frame,174985 .indirect_load_frame,
174839 .indirect_mask,174986 .indirect_mask,
...@@ -174944,6 +175091,7 @@ fn genBinOpMir(...@@ -174944,6 +175091,7 @@ fn genBinOpMir(
174944 .dead,175091 .dead,
174945 .undef,175092 .undef,
174946 .register_overflow,175093 .register_overflow,
175094 .register_pair_overflow,
174947 .register_mask,175095 .register_mask,
174948 .indirect_load_frame,175096 .indirect_load_frame,
174949 .indirect_mask,175097 .indirect_mask,
...@@ -178053,6 +178201,7 @@ fn genCopy(self: *CodeGen, ty: Type, dst_mcv: MCValue, src_mcv: MCValue, opts: C...@@ -178053,6 +178201,7 @@ fn genCopy(self: *CodeGen, ty: Type, dst_mcv: MCValue, src_mcv: MCValue, opts: C
178053 .immediate,178201 .immediate,
178054 .eflags,178202 .eflags,
178055 .register_overflow,178203 .register_overflow,
178204 .register_pair_overflow,
178056 .register_mask,178205 .register_mask,
178057 .indirect_load_frame,178206 .indirect_load_frame,
178058 .indirect_mask,178207 .indirect_mask,
...@@ -178077,6 +178226,7 @@ fn genCopy(self: *CodeGen, ty: Type, dst_mcv: MCValue, src_mcv: MCValue, opts: C...@@ -178077,6 +178226,7 @@ fn genCopy(self: *CodeGen, ty: Type, dst_mcv: MCValue, src_mcv: MCValue, opts: C
178077 .dead,178226 .dead,
178078 .undef,178227 .undef,
178079 .register_overflow,178228 .register_overflow,
178229 .register_pair_overflow,
178080 .register_tee,178230 .register_tee,
178081 .elementwise_gpr,178231 .elementwise_gpr,
178082 .elementwise_sse,178232 .elementwise_sse,
...@@ -178280,6 +178430,7 @@ fn genSetReg(...@@ -178280,6 +178430,7 @@ fn genSetReg(
178280 .ywordwise_sse,178430 .ywordwise_sse,
178281 .zwordwise_sse,178431 .zwordwise_sse,
178282 .reserved_frame,178432 .reserved_frame,
178433 .register_pair_overflow,
178283 => unreachable,178434 => unreachable,
178284 .undef => if (opts.safety) switch (dst_reg.class()) {178435 .undef => if (opts.safety) switch (dst_reg.class()) {
178285 .general_purpose, .gphi => switch (abi_size) {178436 .general_purpose, .gphi => switch (abi_size) {
...@@ -179083,6 +179234,23 @@ fn genSetMem(...@@ -179083,6 +179234,23 @@ fn genSetMem(
179083 @tagName(src_mcv), ty.fmt(zcu),179234 @tagName(src_mcv), ty.fmt(zcu),
179084 }),179235 }),
179085 },179236 },
179237 .register_pair_overflow => |rpo| switch (ty.zigTypeTag(zcu)) {
179238 .optional => {
179239 assert(!ty.optionalReprIsPayload(zcu));
179240 const child_ty = ty.optionalChild(zcu);
179241 try cg.genSetMem(base, disp, child_ty, .{ .register_pair = rpo.pair }, opts);
179242 try cg.genSetMem(
179243 base,
179244 disp + @as(i32, @intCast(child_ty.abiSize(zcu))),
179245 .bool,
179246 .{ .eflags = rpo.eflags },
179247 opts,
179248 );
179249 },
179250 else => return cg.fail("TODO implement genSetMem for {s} of {f}", .{
179251 @tagName(src_mcv), ty.fmt(zcu),
179252 }),
179253 },
179086 .register_offset => |reg_off| {179254 .register_offset => |reg_off| {
179087 const src_reg = cg.copyToTmpRegister(ty, src_mcv) catch |err| switch (err) {179255 const src_reg = cg.copyToTmpRegister(ty, src_mcv) catch |err| switch (err) {
179088 error.OutOfRegisters => {179256 error.OutOfRegisters => {
...@@ -179417,111 +179585,6 @@ fn airBitCast(self: *CodeGen, inst: Air.Inst.Index) !void {...@@ -179417,111 +179585,6 @@ fn airBitCast(self: *CodeGen, inst: Air.Inst.Index) !void {
179417 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });179585 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
179418}179586}
179419179587
179420fn airCmpxchg(self: *CodeGen, inst: Air.Inst.Index) !void {
179421 const pt = self.pt;
179422 const zcu = pt.zcu;
179423 const ty_pl = self.air.instructions.items(.data)[@backingInt(inst)].ty_pl;
179424 const extra = self.air.extraData(Air.Cmpxchg, ty_pl.payload).data;
179425
179426 const dst_ty = self.typeOfIndex(inst);
179427 const ptr_ty = self.typeOf(extra.ptr);
179428 const val_ty = self.typeOf(extra.expected_value);
179429 const val_abi_size: u32 = @intCast(val_ty.abiSize(zcu));
179430
179431 try self.spillRegisters(&.{ .rax, .rdx, .rbx, .rcx });
179432 const regs_lock = self.register_manager.lockRegsAssumeUnused(4, .{ .rax, .rdx, .rbx, .rcx });
179433 defer for (regs_lock) |lock| self.register_manager.unlockReg(lock);
179434
179435 const exp_mcv = try self.resolveInst(extra.expected_value);
179436 if (val_abi_size > 8) {
179437 const exp_addr_mcv: MCValue = switch (exp_mcv) {
179438 .memory, .indirect, .load_frame => exp_mcv.address(),
179439 else => .{ .register = try self.copyToTmpRegister(.usize, exp_mcv.address()) },
179440 };
179441 const exp_addr_lock =
179442 if (exp_addr_mcv.getReg()) |reg| self.register_manager.lockReg(reg) else null;
179443 defer if (exp_addr_lock) |lock| self.register_manager.unlockReg(lock);
179444
179445 try self.genSetReg(.rax, .usize, exp_addr_mcv.deref(), .{});
179446 try self.genSetReg(.rdx, .usize, exp_addr_mcv.offset(8).deref(), .{});
179447 } else try self.genSetReg(.rax, val_ty, exp_mcv, .{});
179448
179449 const new_mcv = try self.resolveInst(extra.new_value);
179450 const new_reg = if (val_abi_size > 8) new: {
179451 const new_addr_mcv: MCValue = switch (new_mcv) {
179452 .memory, .indirect, .load_frame => new_mcv.address(),
179453 else => .{ .register = try self.copyToTmpRegister(.usize, new_mcv.address()) },
179454 };
179455 const new_addr_lock =
179456 if (new_addr_mcv.getReg()) |reg| self.register_manager.lockReg(reg) else null;
179457 defer if (new_addr_lock) |lock| self.register_manager.unlockReg(lock);
179458
179459 try self.genSetReg(.rbx, .usize, new_addr_mcv.deref(), .{});
179460 try self.genSetReg(.rcx, .usize, new_addr_mcv.offset(8).deref(), .{});
179461 break :new null;
179462 } else try self.copyToTmpRegister(val_ty, new_mcv);
179463 const new_lock = if (new_reg) |reg| self.register_manager.lockRegAssumeUnused(reg) else null;
179464 defer if (new_lock) |lock| self.register_manager.unlockReg(lock);
179465
179466 const ptr_mcv = try self.resolveInst(extra.ptr);
179467 const mem_size: Memory.Size = .fromSize(val_abi_size);
179468 const ptr_mem: Memory = switch (ptr_mcv) {
179469 .immediate, .register, .register_offset, .lea_frame => try ptr_mcv.deref().mem(self, .{ .size = mem_size }),
179470 else => .{
179471 .base = .{ .reg = try self.copyToTmpRegister(ptr_ty, ptr_mcv) },
179472 .mod = .{ .rm = .{ .size = mem_size } },
179473 },
179474 };
179475 switch (ptr_mem.mod) {
179476 .rm => {},
179477 .off => return self.fail("TODO airCmpxchg with {s}", .{@tagName(ptr_mcv)}),
179478 }
179479 const ptr_lock = switch (ptr_mem.base) {
179480 .none, .frame, .nav, .uav => null,
179481 .reg => |reg| self.register_manager.lockReg(reg),
179482 .table, .rip_inst, .lazy_sym, .extern_func => unreachable,
179483 };
179484 defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock);
179485
179486 try self.spillEflagsIfOccupied();
179487 const null_reg = if (dst_ty.optionalReprIsPayload(zcu) and !self.liveness.isUnused(inst)) null_reg: {
179488 const null_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
179489 try self.asmRegisterRegister(.{ ._, .xor }, null_reg.to32(), null_reg.to32());
179490 break :null_reg null_reg;
179491 } else null;
179492 const null_lock = if (null_reg) |reg| self.register_manager.lockRegAssumeUnused(reg) else null;
179493 defer if (null_lock) |lock| self.register_manager.unlockReg(lock);
179494
179495 if (val_abi_size <= 8) try self.asmMemoryRegister(
179496 .{ .@"lock _", .cmpxchg },
179497 ptr_mem,
179498 registerAlias(new_reg.?, val_abi_size),
179499 ) else try self.asmMemory(.{ .@"lock _16b", .cmpxchg }, ptr_mem);
179500
179501 const result: MCValue = result: {
179502 if (self.liveness.isUnused(inst)) break :result .unreach;
179503
179504 if (null_reg) |reg| try self.asmCmovccRegisterRegister(
179505 .e,
179506 registerAlias(.rax, val_abi_size),
179507 registerAlias(reg, val_abi_size),
179508 );
179509
179510 if (val_abi_size <= 8) {
179511 if (null_reg) |_| break :result .{ .register = .rax };
179512 self.eflags_inst = inst;
179513 break :result .{ .register_overflow = .{ .reg = .rax, .eflags = .ne } };
179514 }
179515
179516 const dst_mcv = try self.allocRegOrMem(inst, false);
179517 try self.genCopy(.usize, dst_mcv, .{ .register = .rax }, .{});
179518 try self.genCopy(.usize, dst_mcv.address().offset(8).deref(), .{ .register = .rdx }, .{});
179519 if (null_reg == null) try self.genCopy(.bool, dst_mcv.address().offset(16).deref(), .{ .eflags = .ne }, .{});
179520 break :result dst_mcv;
179521 };
179522 return self.finishAir(inst, result, .{ extra.ptr, extra.expected_value, extra.new_value });
179523}
179524
179525fn atomicOp(179588fn atomicOp(
179526 self: *CodeGen,179589 self: *CodeGen,
179527 ptr_mcv: MCValue,179590 ptr_mcv: MCValue,
...@@ -179557,7 +179620,7 @@ fn atomicOp(...@@ -179557,7 +179620,7 @@ fn atomicOp(
179557 };179620 };
179558 switch (ptr_mem.mod) {179621 switch (ptr_mem.mod) {
179559 .rm => {},179622 .rm => {},
179560 .off => return self.fail("TODO airCmpxchg with {s}", .{@tagName(ptr_mcv)}),179623 .off => return self.fail("TODO atomicOp with {s}", .{@tagName(ptr_mcv)}),
179561 }179624 }
179562 const mem_lock = switch (ptr_mem.base) {179625 const mem_lock = switch (ptr_mem.base) {
179563 .none, .frame, .nav, .uav => null,179626 .none, .frame, .nav, .uav => null,
...@@ -181936,6 +181999,7 @@ const Temp = struct {...@@ -181936,6 +181999,7 @@ const Temp = struct {
181936 .register_triple,181999 .register_triple,
181937 .register_quadruple,182000 .register_quadruple,
181938 .register_overflow,182001 .register_overflow,
182002 .register_pair_overflow,
181939 => true,182003 => true,
181940 .load_frame => |frame_addr| !frame_addr.index.isNamed(),182004 .load_frame => |frame_addr| !frame_addr.index.isNamed(),
181941 },182005 },
...@@ -182316,7 +182380,7 @@ const Temp = struct {...@@ -182316,7 +182380,7 @@ const Temp = struct {
182316 return true;182380 return true;
182317 }182381 }
182318182382
182319 fn toPair(first_temp: *Temp, second_temp: *Temp, cg: *CodeGen) InnerError!void {182383 fn toPair(first_temp: *Temp, second_temp: *Temp, result_ty: Type, cg: *CodeGen) InnerError!void {
182320 while (true) for ([_]*Temp{ first_temp, second_temp }) |part_temp| {182384 while (true) for ([_]*Temp{ first_temp, second_temp }) |part_temp| {
182321 if (try part_temp.toRegClass(true, .general_purpose, cg)) break;182385 if (try part_temp.toRegClass(true, .general_purpose, cg)) break;
182322 } else break;182386 } else break;
...@@ -182331,13 +182395,13 @@ const Temp = struct {...@@ -182331,13 +182395,13 @@ const Temp = struct {
182331 assert(cg.reuseTemp(result_temp.index, first_temp.index, first_temp_tracking));182395 assert(cg.reuseTemp(result_temp.index, first_temp.index, first_temp_tracking));
182332 assert(cg.reuseTemp(result_temp.index, second_temp.index, second_temp_tracking));182396 assert(cg.reuseTemp(result_temp.index, second_temp.index, second_temp_tracking));
182333 result_temp_index.tracking(cg).* = .init(result);182397 result_temp_index.tracking(cg).* = .init(result);
182334 cg.temp_type[@backingInt(result_temp_index)] = .slice_const_u8;182398 cg.temp_type[@backingInt(result_temp_index)] = result_ty;
182335 cg.next_temp_index = @fromBackingInt(@intCast(@backingInt(result_temp_index) + 1));182399 cg.next_temp_index = @fromBackingInt(@intCast(@backingInt(result_temp_index) + 1));
182336 first_temp.* = result_temp;182400 first_temp.* = result_temp;
182337 second_temp.* = result_temp;182401 second_temp.* = result_temp;
182338 }182402 }
182339182403
182340 fn withOverflow(temp: *Temp, overflow_temp: *Temp, cg: *CodeGen) InnerError!void {182404 fn withOverflow(temp: *Temp, overflow_temp: *Temp, result_ty: Type, cg: *CodeGen) InnerError!void {
182341 const temp_index = temp.unwrap(cg).temp;182405 const temp_index = temp.unwrap(cg).temp;
182342 const temp_tracking = temp_index.tracking(cg);182406 const temp_tracking = temp_index.tracking(cg);
182343 const overflow_temp_tracking = overflow_temp.unwrap(cg).temp.tracking(cg);182407 const overflow_temp_tracking = overflow_temp.unwrap(cg).temp.tracking(cg);
...@@ -182349,7 +182413,7 @@ const Temp = struct {...@@ -182349,7 +182413,7 @@ const Temp = struct {
182349 assert(cg.reuseTemp(result_temp.index, temp.index, temp_tracking));182413 assert(cg.reuseTemp(result_temp.index, temp.index, temp_tracking));
182350 assert(cg.reuseTemp(result_temp.index, overflow_temp.index, overflow_temp_tracking));182414 assert(cg.reuseTemp(result_temp.index, overflow_temp.index, overflow_temp_tracking));
182351 result_temp_index.tracking(cg).* = .init(.{ .register_overflow = .{ .reg = reg, .eflags = overflow_cc } });182415 result_temp_index.tracking(cg).* = .init(.{ .register_overflow = .{ .reg = reg, .eflags = overflow_cc } });
182352 cg.temp_type[@backingInt(result_temp_index)] = .slice_const_u8;182416 cg.temp_type[@backingInt(result_temp_index)] = result_ty;
182353 cg.next_temp_index = @fromBackingInt(@intCast(@backingInt(result_temp_index) + 1));182417 cg.next_temp_index = @fromBackingInt(@intCast(@backingInt(result_temp_index) + 1));
182354 temp.* = result_temp;182418 temp.* = result_temp;
182355 overflow_temp.* = result_temp;182419 overflow_temp.* = result_temp;
...@@ -182361,7 +182425,22 @@ const Temp = struct {...@@ -182361,7 +182425,22 @@ const Temp = struct {
182361 assert(cg.reuseTemp(result_temp.index, temp.index, temp_tracking));182425 assert(cg.reuseTemp(result_temp.index, temp.index, temp_tracking));
182362 assert(cg.reuseTemp(result_temp.index, overflow_temp.index, overflow_temp_tracking));182426 assert(cg.reuseTemp(result_temp.index, overflow_temp.index, overflow_temp_tracking));
182363 result_temp_index.tracking(cg).* = .init(.{ .register_pair = .{ reg, overflow_reg } });182427 result_temp_index.tracking(cg).* = .init(.{ .register_pair = .{ reg, overflow_reg } });
182364 cg.temp_type[@backingInt(result_temp_index)] = .slice_const_u8;182428 cg.temp_type[@backingInt(result_temp_index)] = result_ty;
182429 cg.next_temp_index = @fromBackingInt(@intCast(@backingInt(result_temp_index) + 1));
182430 temp.* = result_temp;
182431 overflow_temp.* = result_temp;
182432 return;
182433 },
182434 else => {},
182435 },
182436 .register_pair => |pair| switch (overflow_temp_tracking.short) {
182437 .eflags => |overflow_cc| {
182438 const result_temp_index = cg.next_temp_index;
182439 const result_temp: Temp = .{ .index = result_temp_index.toIndex() };
182440 assert(cg.reuseTemp(result_temp.index, temp.index, temp_tracking));
182441 assert(cg.reuseTemp(result_temp.index, overflow_temp.index, overflow_temp_tracking));
182442 result_temp_index.tracking(cg).* = .init(.{ .register_pair_overflow = .{ .pair = pair, .eflags = overflow_cc } });
182443 cg.temp_type[@backingInt(result_temp_index)] = result_ty;
182365 cg.next_temp_index = @fromBackingInt(@intCast(@backingInt(result_temp_index) + 1));182444 cg.next_temp_index = @fromBackingInt(@intCast(@backingInt(result_temp_index) + 1));
182366 temp.* = result_temp;182445 temp.* = result_temp;
182367 overflow_temp.* = result_temp;182446 overflow_temp.* = result_temp;
...@@ -182401,6 +182480,7 @@ const Temp = struct {...@@ -182401,6 +182480,7 @@ const Temp = struct {
182401 .register_triple,182480 .register_triple,
182402 .register_quadruple,182481 .register_quadruple,
182403 .register_overflow,182482 .register_overflow,
182483 .register_pair_overflow,
182404 .register_mask,182484 .register_mask,
182405 .indirect_load_frame,182485 .indirect_load_frame,
182406 .indirect_mask,182486 .indirect_mask,
...@@ -188883,6 +188963,7 @@ const Temp = struct {...@@ -188883,6 +188963,7 @@ const Temp = struct {
188883 .register_quadruple,188963 .register_quadruple,
188884 .register_offset,188964 .register_offset,
188885 .register_overflow,188965 .register_overflow,
188966 .register_pair_overflow,
188886 .register_mask,188967 .register_mask,
188887 .memory,188968 .memory,
188888 .indirect,188969 .indirect,
...@@ -189001,6 +189082,7 @@ fn reuseTemp(...@@ -189001,6 +189082,7 @@ fn reuseTemp(
189001 .register_pair,189082 .register_pair,
189002 .register_offset,189083 .register_offset,
189003 .register_overflow,189084 .register_overflow,
189085 .register_pair_overflow,
189004 .register_mask,189086 .register_mask,
189005 .indirect,189087 .indirect,
189006 => for (tracking.short.getRegs()) |tracked_reg| {189088 => for (tracking.short.getRegs()) |tracked_reg| {
...@@ -189012,7 +189094,10 @@ fn reuseTemp(...@@ -189012,7 +189094,10 @@ fn reuseTemp(
189012 else => {},189094 else => {},
189013 }189095 }
189014 switch (tracking.short) {189096 switch (tracking.short) {
189015 .eflags, .register_overflow => cg.eflags_inst = new_inst,189097 .eflags,
189098 .register_overflow,
189099 .register_pair_overflow,
189100 => cg.eflags_inst = new_inst,
189016 else => {},189101 else => {},
189017 }189102 }
189018 tracking.reuse(cg, new_inst, old_inst);189103 tracking.reuse(cg, new_inst, old_inst);
...@@ -191592,7 +191677,13 @@ const Select = struct {...@@ -191592,7 +191677,13 @@ const Select = struct {
191592 } },191677 } },
191593 .eflags => unreachable,191678 .eflags => unreachable,
191594 .register => |reg| .{ .reg = s.lowerReg(reg.toSize(op.flags.base.size, s.cg.target)) },191679 .register => |reg| .{ .reg = s.lowerReg(reg.toSize(op.flags.base.size, s.cg.target)) },
191595 .register_pair, .register_triple, .register_quadruple, .register_offset, .register_overflow => unreachable,191680 .register_pair,
191681 .register_triple,
191682 .register_quadruple,
191683 .register_offset,
191684 .register_overflow,
191685 .register_pair_overflow,
191686 => unreachable,
191596 .register_mask => |reg_mask| .{ .reg = s.lowerReg(reg_mask.reg.toSize(op.flags.base.size, s.cg.target)) },191687 .register_mask => |reg_mask| .{ .reg = s.lowerReg(reg_mask.reg.toSize(op.flags.base.size, s.cg.target)) },
191597 .lea_nav => |nav| .{ .imm = .{ .nav = .{ .index = nav } } },191688 .lea_nav => |nav| .{ .imm = .{ .nav = .{ .index = nav } } },
191598 .lea_uav => |uav| .{ .imm = .{ .uav = uav } },191689 .lea_uav => |uav| .{ .imm = .{ .uav = uav } },
test/behavior/atomics.zig+7
...@@ -133,6 +133,13 @@ fn test_u128_cmpxchg() !void {...@@ -133,6 +133,13 @@ fn test_u128_cmpxchg() !void {
133133
134 try expect(@cmpxchgStrong(u128, &x, 5678, 42, .seq_cst, .seq_cst) == null);134 try expect(@cmpxchgStrong(u128, &x, 5678, 42, .seq_cst, .seq_cst) == null);
135 try expect(x == 42);135 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);
136}143}
137144
138var a_global_variable = @as(u32, 1234);145var a_global_variable = @as(u32, 1234);