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) {...@@ -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,
...@@ -53047,7 +53067,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -53047,7 +53067,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
53047 }),53067 }),
53048 else => |e| return e,53068 else => |e| return e,
53049 };53069 };
53050 try res[0].withOverflow(&res[1], cg);53070 try res[0].withOverflow(&res[1], ty_pl.ty, cg);
53051 try res[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg);53071 try res[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg);
53052 },53072 },
53053 .sub_with_overflow => |air_tag| {53073 .sub_with_overflow => |air_tag| {
...@@ -53952,7 +53972,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -53952,7 +53972,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
53952 }),53972 }),
53953 else => |e| return e,53973 else => |e| return e,
53954 };53974 };
53955 try res[0].withOverflow(&res[1], cg);53975 try res[0].withOverflow(&res[1], ty_pl.ty, cg);
53956 try res[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg);53976 try res[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg);
53957 },53977 },
53958 .mul_with_overflow => |air_tag| {53978 .mul_with_overflow => |air_tag| {
...@@ -57549,7 +57569,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -57549,7 +57569,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
57549 }),57569 }),
57550 else => |e| return e,57570 else => |e| return e,
57551 };57571 };
57552 try res[0].withOverflow(&res[1], cg);57572 try res[0].withOverflow(&res[1], ty_pl.ty, cg);
57553 try res[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg);57573 try res[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg);
57554 },57574 },
57555 .shl_with_overflow => |air_tag| {57575 .shl_with_overflow => |air_tag| {
...@@ -60894,7 +60914,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -60894,7 +60914,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
60894 }),60914 }),
60895 else => |e| return e,60915 else => |e| return e,
60896 };60916 };
60897 try res[0].withOverflow(&res[1], cg);60917 try res[0].withOverflow(&res[1], ty_pl.ty, cg);
60898 try res[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg);60918 try res[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg);
60899 },60919 },
60900 .alloc => {60920 .alloc => {
...@@ -104070,7 +104090,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -104070,7 +104090,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
104070 const ty_pl = air_datas[@backingInt(inst)].ty_pl;104090 const ty_pl = air_datas[@backingInt(inst)].ty_pl;
104071 const bin_op = cg.air.extraData(Air.Bin, ty_pl.payload).data;104091 const bin_op = cg.air.extraData(Air.Bin, ty_pl.payload).data;
104072 var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs });104092 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);
104074 try ops[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg);104094 try ops[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg);
104075 },104095 },
104076 .slice_len => {104096 .slice_len => {
...@@ -104487,7 +104507,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -104487,7 +104507,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
104487 var len = try cg.tempInit(.usize, .{104507 var len = try cg.tempInit(.usize, .{
104488 .immediate = cg.typeOf(ty_op.operand).childType(zcu).arrayLen(zcu),104508 .immediate = cg.typeOf(ty_op.operand).childType(zcu).arrayLen(zcu),
104489 });104509 });
104490 try ops[0].toPair(&len, cg);104510 try ops[0].toPair(&len, ty_op.ty, cg);
104491 try ops[0].finish(inst, &.{ty_op.operand}, &ops, cg);104511 try ops[0].finish(inst, &.{ty_op.operand}, &ops, cg);
104492 },104512 },
104493 .int_from_float, .int_from_float_optimized => |air_tag| {104513 .int_from_float, .int_from_float_optimized => |air_tag| {
...@@ -171079,7 +171099,113 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -171079,7 +171099,113 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
171079 };171099 };
171080 for (ops) |op| try op.die(cg);171100 for (ops) |op| try op.die(cg);
171081 },171101 },
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 },
171083 .atomic_load => try cg.airAtomicLoad(inst),171209 .atomic_load => try cg.airAtomicLoad(inst),
171084 .atomic_store_unordered => try cg.airAtomicStore(inst, .unordered),171210 .atomic_store_unordered => try cg.airAtomicStore(inst, .unordered),
171085 .atomic_store_monotonic => try cg.airAtomicStore(inst, .monotonic),171211 .atomic_store_monotonic => try cg.airAtomicStore(inst, .monotonic),
...@@ -171421,7 +171547,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -171421,7 +171547,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
171421 }),171547 }),
171422 else => |e| return e,171548 else => |e| return e,
171423 };171549 };
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);
171425 try res[0].finish(inst, &.{un_op}, &ops, cg);171551 try res[0].finish(inst, &.{un_op}, &ops, cg);
171426 },171552 },
171427 .error_name => |air_tag| {171553 .error_name => |air_tag| {
...@@ -171524,7 +171650,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -171524,7 +171650,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
171524 else => |e| return e,171650 else => |e| return e,
171525 };171651 };
171526 const orig_res = res;171652 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);
171528 for (&ops) |*op| for (orig_res) |orig_r| {171654 for (&ops) |*op| for (orig_res) |orig_r| {
171529 if (op.index != orig_r.index) continue;171655 if (op.index != orig_r.index) continue;
171530 op.* = res[0];171656 op.* = res[0];
...@@ -174108,7 +174234,10 @@ fn getValue(self: *CodeGen, value: MCValue, inst: ?Air.Inst.Index) !void {...@@ -174108,7 +174234,10 @@ fn getValue(self: *CodeGen, value: MCValue, inst: ?Air.Inst.Index) !void {
174108 for (value.getRegs()) |reg| try self.register_manager.getReg(reg, inst);174234 for (value.getRegs()) |reg| try self.register_manager.getReg(reg, inst);
174109 switch (value) {174235 switch (value) {
174110 else => {},174236 else => {},
174111 .eflags, .register_overflow => self.eflags_inst = inst,174237 .eflags,
174238 .register_overflow,
174239 .register_pair_overflow,
174240 => self.eflags_inst = inst,
174112 }174241 }
174113}174242}
174114174243
...@@ -174136,6 +174265,10 @@ fn freeValue(self: *CodeGen, value: MCValue, comptime opts: FreeOptions) !void {...@@ -174136,6 +174265,10 @@ fn freeValue(self: *CodeGen, value: MCValue, comptime opts: FreeOptions) !void {
174136 try self.freeReg(reg_ov.reg, opts);174265 try self.freeReg(reg_ov.reg, opts);
174137 self.eflags_inst = null;174266 self.eflags_inst = null;
174138 },174267 },
174268 .register_pair_overflow => |rpo| {
174269 for (rpo.pair) |r| try self.freeReg(r, opts);
174270 self.eflags_inst = null;
174271 },
174139 .register_mask => |reg_mask| try self.freeReg(reg_mask.reg, opts),174272 .register_mask => |reg_mask| try self.freeReg(reg_mask.reg, opts),
174140 .eflags => self.eflags_inst = null,174273 .eflags => self.eflags_inst = null,
174141 else => {}, // TODO process stack allocation death174274 else => {}, // TODO process stack allocation death
...@@ -174678,7 +174811,12 @@ fn reuseOperandAdvanced(...@@ -174678,7 +174811,12 @@ fn reuseOperandAdvanced(
174678 return false;174811 return false;
174679174812
174680 switch (mcv) {174813 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| {
174682 // If it's in the registers table, need to associate the register(s) with the174820 // If it's in the registers table, need to associate the register(s) with the
174683 // new instruction.174821 // new instruction.
174684 if (maybe_tracked_inst) |tracked_inst| {174822 if (maybe_tracked_inst) |tracked_inst| {
...@@ -174693,7 +174831,10 @@ fn reuseOperandAdvanced(...@@ -174693,7 +174831,10 @@ fn reuseOperandAdvanced(
174693 else => return false,174831 else => return false,
174694 }174832 }
174695 switch (mcv) {174833 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,
174697 else => {},174838 else => {},
174698 }174839 }
174699174840
...@@ -174720,6 +174861,7 @@ fn load(self: *CodeGen, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerE...@@ -174720,6 +174861,7 @@ fn load(self: *CodeGen, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerE
174720 .register_triple,174861 .register_triple,
174721 .register_quadruple,174862 .register_quadruple,
174722 .register_overflow,174863 .register_overflow,
174864 .register_pair_overflow,
174723 .register_mask,174865 .register_mask,
174724 .indirect_load_frame,174866 .indirect_load_frame,
174725 .indirect_mask,174867 .indirect_mask,
...@@ -174779,6 +174921,7 @@ fn store(...@@ -174779,6 +174921,7 @@ fn store(
174779 .register_triple,174921 .register_triple,
174780 .register_quadruple,174922 .register_quadruple,
174781 .register_overflow,174923 .register_overflow,
174924 .register_pair_overflow,
174782 .register_mask,174925 .register_mask,
174783 .indirect_load_frame,174926 .indirect_load_frame,
174784 .indirect_mask,174927 .indirect_mask,
...@@ -174830,6 +174973,7 @@ fn genUnOpMir(self: *CodeGen, mir_tag: Mir.Inst.FixedTag, dst_ty: Type, dst_mcv:...@@ -174830,6 +174973,7 @@ fn genUnOpMir(self: *CodeGen, mir_tag: Mir.Inst.FixedTag, dst_ty: Type, dst_mcv:
174830 .register_offset,174973 .register_offset,
174831 .eflags,174974 .eflags,
174832 .register_overflow,174975 .register_overflow,
174976 .register_pair_overflow,
174833 .register_mask,174977 .register_mask,
174834 .indirect_load_frame,174978 .indirect_load_frame,
174835 .indirect_mask,174979 .indirect_mask,
...@@ -175526,6 +175670,7 @@ fn genBinOpMir(...@@ -175526,6 +175670,7 @@ fn genBinOpMir(
175526 .immediate,175670 .immediate,
175527 .eflags,175671 .eflags,
175528 .register_overflow,175672 .register_overflow,
175673 .register_pair_overflow,
175529 .register_mask,175674 .register_mask,
175530 .lea_frame,175675 .lea_frame,
175531 .lea_nav,175676 .lea_nav,
...@@ -175574,6 +175719,7 @@ fn genBinOpMir(...@@ -175574,6 +175719,7 @@ fn genBinOpMir(
175574 .dead,175719 .dead,
175575 .undef,175720 .undef,
175576 .register_overflow,175721 .register_overflow,
175722 .register_pair_overflow,
175577 .register_mask,175723 .register_mask,
175578 .indirect_load_frame,175724 .indirect_load_frame,
175579 .indirect_mask,175725 .indirect_mask,
...@@ -175752,6 +175898,7 @@ fn genBinOpMir(...@@ -175752,6 +175898,7 @@ fn genBinOpMir(
175752 .dead,175898 .dead,
175753 .undef,175899 .undef,
175754 .register_overflow,175900 .register_overflow,
175901 .register_pair_overflow,
175755 .register_mask,175902 .register_mask,
175756 .indirect_load_frame,175903 .indirect_load_frame,
175757 .indirect_mask,175904 .indirect_mask,
...@@ -175862,6 +176009,7 @@ fn genBinOpMir(...@@ -175862,6 +176009,7 @@ fn genBinOpMir(
175862 .dead,176009 .dead,
175863 .undef,176010 .undef,
175864 .register_overflow,176011 .register_overflow,
176012 .register_pair_overflow,
175865 .register_mask,176013 .register_mask,
175866 .indirect_load_frame,176014 .indirect_load_frame,
175867 .indirect_mask,176015 .indirect_mask,
...@@ -178971,6 +179119,7 @@ fn genCopy(self: *CodeGen, ty: Type, dst_mcv: MCValue, src_mcv: MCValue, opts: C...@@ -178971,6 +179119,7 @@ fn genCopy(self: *CodeGen, ty: Type, dst_mcv: MCValue, src_mcv: MCValue, opts: C
178971 .immediate,179119 .immediate,
178972 .eflags,179120 .eflags,
178973 .register_overflow,179121 .register_overflow,
179122 .register_pair_overflow,
178974 .register_mask,179123 .register_mask,
178975 .indirect_load_frame,179124 .indirect_load_frame,
178976 .indirect_mask,179125 .indirect_mask,
...@@ -178995,6 +179144,7 @@ fn genCopy(self: *CodeGen, ty: Type, dst_mcv: MCValue, src_mcv: MCValue, opts: C...@@ -178995,6 +179144,7 @@ fn genCopy(self: *CodeGen, ty: Type, dst_mcv: MCValue, src_mcv: MCValue, opts: C
178995 .dead,179144 .dead,
178996 .undef,179145 .undef,
178997 .register_overflow,179146 .register_overflow,
179147 .register_pair_overflow,
178998 .register_tee,179148 .register_tee,
178999 .elementwise_gpr,179149 .elementwise_gpr,
179000 .elementwise_sse,179150 .elementwise_sse,
...@@ -179198,6 +179348,7 @@ fn genSetReg(...@@ -179198,6 +179348,7 @@ fn genSetReg(
179198 .ywordwise_sse,179348 .ywordwise_sse,
179199 .zwordwise_sse,179349 .zwordwise_sse,
179200 .reserved_frame,179350 .reserved_frame,
179351 .register_pair_overflow,
179201 => unreachable,179352 => unreachable,
179202 .undef => if (opts.safety) switch (dst_reg.class()) {179353 .undef => if (opts.safety) switch (dst_reg.class()) {
179203 .general_purpose, .gphi => switch (abi_size) {179354 .general_purpose, .gphi => switch (abi_size) {
...@@ -180001,6 +180152,23 @@ fn genSetMem(...@@ -180001,6 +180152,23 @@ fn genSetMem(
180001 @tagName(src_mcv), ty.fmt(zcu),180152 @tagName(src_mcv), ty.fmt(zcu),
180002 }),180153 }),
180003 },180154 },
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 },
180004 .register_offset => |reg_off| {180172 .register_offset => |reg_off| {
180005 const src_reg = cg.copyToTmpRegister(ty, src_mcv) catch |err| switch (err) {180173 const src_reg = cg.copyToTmpRegister(ty, src_mcv) catch |err| switch (err) {
180006 error.OutOfRegisters => {180174 error.OutOfRegisters => {
...@@ -180335,111 +180503,6 @@ fn airBitCast(self: *CodeGen, inst: Air.Inst.Index) !void {...@@ -180335,111 +180503,6 @@ fn airBitCast(self: *CodeGen, inst: Air.Inst.Index) !void {
180335 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });180503 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
180336}180504}
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
180443fn atomicOp(180506fn atomicOp(
180444 self: *CodeGen,180507 self: *CodeGen,
180445 ptr_mcv: MCValue,180508 ptr_mcv: MCValue,
...@@ -180475,7 +180538,7 @@ fn atomicOp(...@@ -180475,7 +180538,7 @@ fn atomicOp(
180475 };180538 };
180476 switch (ptr_mem.mod) {180539 switch (ptr_mem.mod) {
180477 .rm => {},180540 .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)}),
180479 }180542 }
180480 const mem_lock = switch (ptr_mem.base) {180543 const mem_lock = switch (ptr_mem.base) {
180481 .none, .frame, .nav, .uav => null,180544 .none, .frame, .nav, .uav => null,
...@@ -182855,6 +182918,7 @@ const Temp = struct {...@@ -182855,6 +182918,7 @@ const Temp = struct {
182855 .register_triple,182918 .register_triple,
182856 .register_quadruple,182919 .register_quadruple,
182857 .register_overflow,182920 .register_overflow,
182921 .register_pair_overflow,
182858 => true,182922 => true,
182859 .load_frame => |frame_addr| !frame_addr.index.isNamed(),182923 .load_frame => |frame_addr| !frame_addr.index.isNamed(),
182860 },182924 },
...@@ -183235,7 +183299,7 @@ const Temp = struct {...@@ -183235,7 +183299,7 @@ const Temp = struct {
183235 return true;183299 return true;
183236 }183300 }
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 {
183239 while (true) for ([_]*Temp{ first_temp, second_temp }) |part_temp| {183303 while (true) for ([_]*Temp{ first_temp, second_temp }) |part_temp| {
183240 if (try part_temp.toRegClass(true, .general_purpose, cg)) break;183304 if (try part_temp.toRegClass(true, .general_purpose, cg)) break;
183241 } else break;183305 } else break;
...@@ -183250,13 +183314,13 @@ const Temp = struct {...@@ -183250,13 +183314,13 @@ const Temp = struct {
183250 assert(cg.reuseTemp(result_temp.index, first_temp.index, first_temp_tracking));183314 assert(cg.reuseTemp(result_temp.index, first_temp.index, first_temp_tracking));
183251 assert(cg.reuseTemp(result_temp.index, second_temp.index, second_temp_tracking));183315 assert(cg.reuseTemp(result_temp.index, second_temp.index, second_temp_tracking));
183252 result_temp_index.tracking(cg).* = .init(result);183316 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;
183254 cg.next_temp_index = @fromBackingInt(@intCast(@backingInt(result_temp_index) + 1));183318 cg.next_temp_index = @fromBackingInt(@intCast(@backingInt(result_temp_index) + 1));
183255 first_temp.* = result_temp;183319 first_temp.* = result_temp;
183256 second_temp.* = result_temp;183320 second_temp.* = result_temp;
183257 }183321 }
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 {
183260 const temp_index = temp.unwrap(cg).temp;183324 const temp_index = temp.unwrap(cg).temp;
183261 const temp_tracking = temp_index.tracking(cg);183325 const temp_tracking = temp_index.tracking(cg);
183262 const overflow_temp_tracking = overflow_temp.unwrap(cg).temp.tracking(cg);183326 const overflow_temp_tracking = overflow_temp.unwrap(cg).temp.tracking(cg);
...@@ -183268,7 +183332,7 @@ const Temp = struct {...@@ -183268,7 +183332,7 @@ const Temp = struct {
183268 assert(cg.reuseTemp(result_temp.index, temp.index, temp_tracking));183332 assert(cg.reuseTemp(result_temp.index, temp.index, temp_tracking));
183269 assert(cg.reuseTemp(result_temp.index, overflow_temp.index, overflow_temp_tracking));183333 assert(cg.reuseTemp(result_temp.index, overflow_temp.index, overflow_temp_tracking));
183270 result_temp_index.tracking(cg).* = .init(.{ .register_overflow = .{ .reg = reg, .eflags = overflow_cc } });183334 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;
183272 cg.next_temp_index = @fromBackingInt(@intCast(@backingInt(result_temp_index) + 1));183336 cg.next_temp_index = @fromBackingInt(@intCast(@backingInt(result_temp_index) + 1));
183273 temp.* = result_temp;183337 temp.* = result_temp;
183274 overflow_temp.* = result_temp;183338 overflow_temp.* = result_temp;
...@@ -183280,7 +183344,22 @@ const Temp = struct {...@@ -183280,7 +183344,22 @@ const Temp = struct {
183280 assert(cg.reuseTemp(result_temp.index, temp.index, temp_tracking));183344 assert(cg.reuseTemp(result_temp.index, temp.index, temp_tracking));
183281 assert(cg.reuseTemp(result_temp.index, overflow_temp.index, overflow_temp_tracking));183345 assert(cg.reuseTemp(result_temp.index, overflow_temp.index, overflow_temp_tracking));
183282 result_temp_index.tracking(cg).* = .init(.{ .register_pair = .{ reg, overflow_reg } });183346 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;
183284 cg.next_temp_index = @fromBackingInt(@intCast(@backingInt(result_temp_index) + 1));183363 cg.next_temp_index = @fromBackingInt(@intCast(@backingInt(result_temp_index) + 1));
183285 temp.* = result_temp;183364 temp.* = result_temp;
183286 overflow_temp.* = result_temp;183365 overflow_temp.* = result_temp;
...@@ -183320,6 +183399,7 @@ const Temp = struct {...@@ -183320,6 +183399,7 @@ const Temp = struct {
183320 .register_triple,183399 .register_triple,
183321 .register_quadruple,183400 .register_quadruple,
183322 .register_overflow,183401 .register_overflow,
183402 .register_pair_overflow,
183323 .register_mask,183403 .register_mask,
183324 .indirect_load_frame,183404 .indirect_load_frame,
183325 .indirect_mask,183405 .indirect_mask,
...@@ -189802,6 +189882,7 @@ const Temp = struct {...@@ -189802,6 +189882,7 @@ const Temp = struct {
189802 .register_quadruple,189882 .register_quadruple,
189803 .register_offset,189883 .register_offset,
189804 .register_overflow,189884 .register_overflow,
189885 .register_pair_overflow,
189805 .register_mask,189886 .register_mask,
189806 .memory,189887 .memory,
189807 .indirect,189888 .indirect,
...@@ -189920,6 +190001,7 @@ fn reuseTemp(...@@ -189920,6 +190001,7 @@ fn reuseTemp(
189920 .register_pair,190001 .register_pair,
189921 .register_offset,190002 .register_offset,
189922 .register_overflow,190003 .register_overflow,
190004 .register_pair_overflow,
189923 .register_mask,190005 .register_mask,
189924 .indirect,190006 .indirect,
189925 => for (tracking.short.getRegs()) |tracked_reg| {190007 => for (tracking.short.getRegs()) |tracked_reg| {
...@@ -189931,7 +190013,10 @@ fn reuseTemp(...@@ -189931,7 +190013,10 @@ fn reuseTemp(
189931 else => {},190013 else => {},
189932 }190014 }
189933 switch (tracking.short) {190015 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,
189935 else => {},190020 else => {},
189936 }190021 }
189937 tracking.reuse(cg, new_inst, old_inst);190022 tracking.reuse(cg, new_inst, old_inst);
...@@ -192514,7 +192599,13 @@ const Select = struct {...@@ -192514,7 +192599,13 @@ const Select = struct {
192514 } },192599 } },
192515 .eflags => unreachable,192600 .eflags => unreachable,
192516 .register => |reg| .{ .reg = s.lowerReg(reg.toSize(op.flags.base.size, s.cg.target)) },192601 .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,
192518 .register_mask => |reg_mask| .{ .reg = s.lowerReg(reg_mask.reg.toSize(op.flags.base.size, s.cg.target)) },192609 .register_mask => |reg_mask| .{ .reg = s.lowerReg(reg_mask.reg.toSize(op.flags.base.size, s.cg.target)) },
192519 .lea_nav => |nav| .{ .imm = .{ .nav = .{ .index = nav } } },192610 .lea_nav => |nav| .{ .imm = .{ .nav = .{ .index = nav } } },
192520 .lea_uav => |uav| .{ .imm = .{ .uav = uav } },192611 .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);