authorgravatar for xtex@astrafall.orgxtex <xtex@astrafall.org> 2026-09-30 19:51:59+08:00
committergravatar for xtex@astrafall.orgxtex <xtex@astrafall.org> 2026-10-01 08:39:15+08:00
logbfaaeab734e6cef3cd2a1186e14a19ba8481ec6e
treee666248bb6a99bb3b632b429199879cefc9c6d86
parente2d0773388ae25b3a55787968a5a652efdae07ea
signature Signed by SSH key SHA256:IEYEjkZlkUTr5U9GiDAmZU/4eZus2t2RsxusyhQqwao

loongarch: support unordered load/store atomicity


1 files changed, 84 insertions(+), 28 deletions(-)

src/codegen/loongarch/Select.zig+84-28
......@@ -983,7 +983,14 @@ pub const Value = struct {
983983 while (part_it.next()) |part_vi| {
984984 const part_offset, const part_size = part_vi.positionInParent(isel);
985985 const part_mat = try part_vi.mat(isel, .{});
986 try isel.moveLoc(def_loc, part_offset, part_mat.loc(), 0, part_size, .preserved);
986 try isel.moveLoc(
987 def_loc,
988 part_offset,
989 part_mat.loc(),
990 0,
991 part_size,
992 .{ .dst_prot = .preserved },
993 );
987994 try part_mat.finish(isel);
988995 if (reextend_parts)
989996 try part_vi.reextend(isel, def_ext);
......@@ -1003,7 +1010,7 @@ pub const Value = struct {
10031010 const def_loc = def_vi.takeLocationMarkWritten(isel) orelse return;
10041011 const def_offset, const def_size = def_vi.positionInParent(isel);
10051012 const parent_mat = try parent_vi.mat(isel, .{});
1006 try isel.moveLoc(def_loc, 0, parent_mat.loc(), def_offset, def_size, .none);
1013 try isel.moveLoc(def_loc, 0, parent_mat.loc(), def_offset, def_size, .{});
10071014 try parent_mat.finish(isel);
10081015 }
10091016
......@@ -1115,7 +1122,7 @@ pub const Value = struct {
11151122 .{ .stack_slot = .{ .base = base_reg, .offset = 0 } },
11161123 offset,
11171124 vi.size(isel),
1118 .none,
1125 .{ .atomic = opts.atomic },
11191126 );
11201127 return true;
11211128 }
......@@ -1167,7 +1174,7 @@ pub const Value = struct {
11671174 const src_loc = src_mat.loc();
11681175 if (!std.meta.eql(loc, src_loc)) {
11691176 loc.markRegWritten(isel);
1170 try isel.moveLoc(loc, 0, src_mat.loc(), 0, copy_size, .none);
1177 try isel.moveLoc(loc, 0, src_mat.loc(), 0, copy_size, .{});
11711178 }
11721179 try src_mat.finish(isel);
11731180 }
......@@ -1271,7 +1278,14 @@ pub const Value = struct {
12711278 _ = try isel.fillReg(src_reg);
12721279 }
12731280 // TODO: replace reextending def_part_vi to .zero_ext with moveLoc .wipe when applicable
1274 try isel.moveLoc(def_part_loc, dst_offset, src_loc, src_offset, mat_size, .preserved);
1281 try isel.moveLoc(
1282 def_part_loc,
1283 dst_offset,
1284 src_loc,
1285 src_offset,
1286 mat_size,
1287 .{ .dst_prot = .preserved },
1288 );
12751289 }
12761290 }
12771291 if (maybe_def_addr_mat) |def_addr_mat| try def_addr_mat.finish(isel);
......@@ -1279,6 +1293,8 @@ pub const Value = struct {
12791293
12801294 const MemoryAccessOptions = struct {
12811295 @"volatile": bool = false,
1296 // Atomicity. This is unrelated to ordering and visibility.
1297 atomic: bool = false,
12821298 };
12831299
12841300 const MatOptions = struct {
......@@ -1478,7 +1494,6 @@ pub const Value = struct {
14781494 opts: MemoryAccessOptions,
14791495 ) !void {
14801496 wip_mir_log.debug(" | # store {f} -> [${t}, #{d}]", .{ vi, base_reg, offset });
1481 _ = opts;
14821497
14831498 const hint_stack: Indirect = if (std.math.cast(@FieldType(Indirect, "offset"), offset)) |stack_off|
14841499 .{ .base = base_reg, .offset = stack_off }
......@@ -1491,7 +1506,7 @@ pub const Value = struct {
14911506 value_mat.loc(),
14921507 0,
14931508 vi.size(isel),
1494 .none,
1509 .{ .atomic = opts.atomic },
14951510 );
14961511 try value_mat.finish(isel);
14971512 }
......@@ -1543,7 +1558,7 @@ pub const Value = struct {
15431558 .size = @intCast(part_size),
15441559 .extension = part_vi.extension(isel),
15451560 });
1546 try isel.moveLoc(.{ .register = part_ra }, 0, value_mat.loc(), 0, part_size, .none);
1561 try isel.moveLoc(.{ .register = part_ra }, 0, value_mat.loc(), 0, part_size, .{});
15471562 try value_mat.finish(isel);
15481563 }
15491564
......@@ -1558,7 +1573,7 @@ pub const Value = struct {
15581573 .size = @intCast(part_size),
15591574 .extension = part_vi.extension(isel),
15601575 });
1561 try isel.moveLoc(.{ .stack_slot = layout_part_stack }, 0, value_mat.loc(), 0, part_size, .none);
1576 try isel.moveLoc(.{ .stack_slot = layout_part_stack }, 0, value_mat.loc(), 0, part_size, .{});
15621577 try value_mat.finish(isel);
15631578 }
15641579 }
......@@ -1569,7 +1584,7 @@ pub const Value = struct {
15691584 if (src_loc.asRegister()) |src_reg| _ = try isel.fillReg(src_reg);
15701585 tracking_log.debug("{f} -> {f} (move to)", .{ vi, src_loc });
15711586 if (vi.takeLocationMarkWritten(isel)) |dst_loc|
1572 try isel.moveLoc(dst_loc, 0, src_loc, 0, vi.size(isel), .none);
1587 try isel.moveLoc(dst_loc, 0, src_loc, 0, vi.size(isel), .{});
15731588 if (vi.isSmall(isel)) {
15741589 vi.setSmallLocation(isel, src_loc);
15751590 if (src_loc.asRegister()) |src_reg| {
......@@ -2130,7 +2145,7 @@ pub const Value = struct {
21302145 vi_loc,
21312146 mat.offset,
21322147 mat.size,
2133 .preserved,
2148 .{ .dst_prot = .preserved },
21342149 );
21352150 if (maybe_loc_reg) |loc_reg| {
21362151 const loc_live = isel.live_registers.getPtr(loc_reg);
......@@ -2190,7 +2205,7 @@ pub const Value = struct {
21902205 .{ .stack_slot = .{ .base = addr_mat.reg(), .offset = 0 } },
21912206 offset_from_root + mat.offset,
21922207 mat.size,
2193 .none,
2208 .{},
21942209 );
21952210 try addr_mat.finish(isel);
21962211 },
......@@ -3415,7 +3430,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) codegen.Error!void
34153430 output_loc,
34163431 0,
34173432 output_vi.value.size(isel),
3418 .none,
3433 .{},
34193434 );
34203435 }
34213436 break :output_reg output_reg;
......@@ -3544,7 +3559,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) codegen.Error!void
35443559 const dst_loc: Value.Location = .{ .register = input_ra };
35453560 if (!std.meta.eql(input_val_loc, dst_loc)) {
35463561 dst_loc.markRegWritten(isel);
3547 try isel.moveLoc(dst_loc, 0, input_val_loc, 0, input_ra.mod.byteSize(isel.target), .none);
3562 try isel.moveLoc(dst_loc, 0, input_val_loc, 0, input_ra.mod.byteSize(isel.target), .{});
35483563 }
35493564 try input_val_mat.finish(isel);
35503565 },
......@@ -3962,6 +3977,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) codegen.Error!void
39623977 const ptr_mat = try ptr_vi.matReg(isel);
39633978 try src_vi.matStore(isel, ptr_mat.reg(), 0, .{
39643979 .@"volatile" = ptr_info.flags.is_volatile,
3980 .atomic = (air_tag == .atomic_store_unordered),
39653981 });
39663982 try ptr_mat.finish(isel);
39673983 },
......@@ -3986,6 +4002,29 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) codegen.Error!void
39864002 try ptr_mat.finish(isel);
39874003 }
39884004 },
4005 .atomic_load => {
4006 const atomic_load = air.data(air.inst_index).atomic_load;
4007 const ptr_ty = isel.air.typeOf(atomic_load.ptr, ip);
4008 const ptr_info = ptr_ty.ptrInfo(zcu);
4009 if (ptr_info.packed_offset.host_size > 0) return isel.fail("packed atomic load", .{});
4010
4011 if (ptr_info.flags.is_volatile)
4012 _ = try isel.use(air.inst_index.toRef());
4013 if (isel.live_values.fetchRemove(air.inst_index)) |dst_vi| {
4014 defer dst_vi.value.deref(isel);
4015
4016 const ptr_vi = try isel.use(atomic_load.ptr);
4017 const ptr_mat = try ptr_vi.matIntRegZeroExt(isel);
4018 _ = try dst_vi.value.defLoad(isel, ptr_mat.reg(), 0, .{
4019 .@"volatile" = ptr_info.flags.is_volatile,
4020 .atomic = true,
4021 });
4022 try ptr_mat.finish(isel);
4023
4024 if (atomic_load.order != .unordered)
4025 try isel.failUnimplemented("ordered atomic load", .{});
4026 }
4027 },
39894028 .int_cast => if (isel.live_values.fetchRemove(air.inst_index)) |dst_vi| {
39904029 defer dst_vi.value.deref(isel);
39914030
......@@ -6206,6 +6245,11 @@ fn elemPtr(
62066245 try index_mat.finish(isel);
62076246}
62086247
6248const MoveLocOptions = struct {
6249 dst_prot: DestProtection = .none,
6250 atomic: bool = false,
6251};
6252
62096253fn moveLoc(
62106254 isel: *Select,
62116255 dst_loc: Value.Location,
......@@ -6213,22 +6257,23 @@ fn moveLoc(
62136257 src_loc: Value.Location,
62146258 src_off: u64,
62156259 size: u64,
6216 dst_prot: DestProtection,
6260 opts: MoveLocOptions,
62176261) !void {
62186262 if (dst_loc.isUnallocated()) return;
62196263 if (std.meta.eql(dst_loc, src_loc) and dst_off == src_off) return;
62206264 if (size == 0) return;
62216265 assert(!src_loc.isUnallocated());
6222 wip_mir_log.debug(" | # move {f}[{d}] <- {f}[{d}], {d}B, dst prot={t}", .{
6266 wip_mir_log.debug(" | # move {f}[{d}] <- {f}[{d}], {d}B, dst prot={t}, atomic={}", .{
62236267 dst_loc,
62246268 dst_off,
62256269 src_loc,
62266270 src_off,
62276271 size,
6228 dst_prot,
6272 opts.dst_prot,
6273 opts.atomic,
62296274 });
62306275
6231 const dst_lock: RegLock = if (dst_prot != .preserved) .empty else dst_loc.tryLock(isel);
6276 const dst_lock: RegLock = if (opts.dst_prot != .preserved) .empty else dst_loc.tryLock(isel);
62326277 defer dst_lock.unlock(isel);
62336278 const src_lock = src_loc.tryLock(isel);
62346279 defer src_lock.unlock(isel);
......@@ -6241,7 +6286,7 @@ fn moveLoc(
62416286 src_ra,
62426287 @intCast(src_off * 8),
62436288 @intCast(size * 8),
6244 dst_prot,
6289 opts.dst_prot,
62456290 ),
62466291 .stack_slot => |src_stack| {
62476292 const tmp_reg = if (dst_ra.mod == .integer and dst_off == 0)
......@@ -6255,7 +6300,7 @@ fn moveLoc(
62556300 .{ .reg = tmp_reg, .mod = .integer },
62566301 0,
62576302 @intCast(size * 8),
6258 dst_prot,
6303 opts.dst_prot,
62596304 );
62606305 try isel.loadReg(
62616306 tmp_reg,
......@@ -6267,6 +6312,8 @@ fn moveLoc(
62676312 },
62686313 },
62696314 .stack_slot => |dst_stack| {
6315 if (size > isel.gprSize() and opts.atomic)
6316 return isel.fail("Memory store of {d} bytes cannot be atomic", .{size});
62706317 if (size > isel.gprSize()) {
62716318 // large memory copies, src must be stack_slot
62726319 const src_stack = src_loc.stack_slot;
......@@ -6294,7 +6341,7 @@ fn moveLoc(
62946341 if (gen_low_to_high) {
62956342 var off: u64 = 0;
62966343 for (0..@intCast(steps)) |_| {
6297 try isel.moveLoc(dst_loc, dst_off + off, src_loc, src_off + off, gpr_size, dst_prot);
6344 try isel.moveLoc(dst_loc, dst_off + off, src_loc, src_off + off, gpr_size, opts);
62986345 off += gpr_size;
62996346 }
63006347 }
......@@ -6302,7 +6349,7 @@ fn moveLoc(
63026349 var off: u64 = steps * gpr_size;
63036350 for (0..@intCast(steps)) |_| {
63046351 off -= gpr_size;
6305 try isel.moveLoc(dst_loc, dst_off + off, src_loc, src_off + off, gpr_size, dst_prot);
6352 try isel.moveLoc(dst_loc, dst_off + off, src_loc, src_off + off, gpr_size, opts);
63066353 }
63076354 }
63086355
......@@ -6313,7 +6360,9 @@ fn moveLoc(
63136360 // If size is not direct mem op size and !kill_dst, old values have to
63146361 // be loaded first.
63156362 const memop_size = memOpSizeFitting(size);
6316 const need_load = memop_size != size;
6363 const need_load = memop_size != size and opts.dst_prot != .none;
6364 if (opts.atomic and need_load)
6365 return isel.fail("Memory store of {d} bytes cannot be atomic without killing other bytes", .{size});
63176366 const tmp_reg, const tmp_allocated = tmp_reg: {
63186367 if (src_off == 0 and !need_load) {
63196368 switch (src_loc) {
......@@ -6335,7 +6384,7 @@ fn moveLoc(
63356384 src_loc,
63366385 src_off,
63376386 size,
6338 if (need_load) .preserved else .none,
6387 opts,
63396388 );
63406389 if (need_load)
63416390 try isel.loadReg(tmp_reg, memop_size, .unsigned, dst_stack.base, dst_stack_off);
......@@ -6463,7 +6512,7 @@ fn moveConstant(isel: *Select, dst: Value.Location, init_constant: Constant, ini
64636512 const rd = dst_ra.reg;
64646513 defer if (use_tmp_reg) isel.freeReg(rd);
64656514
6466 if (use_tmp_reg) try isel.moveLoc(dst, 0, .{ .register = dst_ra }, 0, isel.gprSize(), .none);
6515 if (use_tmp_reg) try isel.moveLoc(dst, 0, .{ .register = dst_ra }, 0, isel.gprSize(), .{});
64676516 return switch (ptr.base_addr) {
64686517 .nav => |nav| if (ZigType.fromInterned(ip.getNav(nav).resolved.?.type).isRuntimeFnOrHasRuntimeBits(zcu)) {
64696518 // TODO code model
......@@ -6724,7 +6773,14 @@ fn moveConstant(isel: *Select, dst: Value.Location, init_constant: Constant, ini
67246773 ),
67256774 };
67266775
6727 try isel.moveLoc(dst, part_offset, .{ .register = tmp_ra }, 0, part_size, .preserved);
6776 try isel.moveLoc(
6777 dst,
6778 part_offset,
6779 .{ .register = tmp_ra },
6780 0,
6781 part_size,
6782 .{ .dst_prot = .preserved },
6783 );
67286784 try isel.moveIntImm(tmp_reg, @bitCast(part_value));
67296785
67306786 if (part_offset == 0) break else part_offset -= gpr_size;
......@@ -6755,7 +6811,7 @@ fn moveConstant(isel: *Select, dst: Value.Location, init_constant: Constant, ini
67556811 .{ .stack_slot = .{ .base = tmp_reg, .offset = 0 } },
67566812 offset,
67576813 size,
6758 .none,
6814 .{},
67596815 );
67606816
67616817 // load constant pointer
......@@ -7237,7 +7293,7 @@ const call = struct {
72377293 .{ .register = .{ .mod = .integer, .reg = tmp_reg } },
72387294 0,
72397295 isel.gprSize(),
7240 .preserved,
7296 .{ .dst_prot = .preserved },
72417297 );
72427298 } else unreachable;
72437299 }