atomics A.1a: RMW ops + recognizer + methods, emit bails (lock)

fetch_add/sub/and/or/xor/min/max wired end-to-end except LLVM emission (bails
loudly; A.1b makes it real). New IR op atomic_rmw + RmwKind (no nand) +
AtomicRmw{ptr, operand, val_ty, ordering, kind}. print arm; comptime_vm arm
implements real single-thread RMW (load/compute/store/return-old, signed|unsigned
min/max from val_ty). Recognizer extended (rmwKindFromName) — RMW restricted to
integer T (float fadd / pointer RMW out of scope, rejected loudly); all orderings
valid for RMW. Methods fetch_* on Atomic($T) with comptime $o: Ordering.
examples/1701 locked to the bail. Suite green (716/0).
This commit is contained in:
agra
2026-06-20 10:14:49 +03:00
parent acf31839ea
commit 718f27e27f
11 changed files with 138 additions and 3 deletions

View File

@@ -684,6 +684,33 @@ pub const Vm = struct {
try self.writeField(table, frame.get(a.ptr.index()), vty, frame.get(a.val.index()));
return .{ .value = 0 };
},
// RMW at comptime (single-thread): load old, compute new, store new,
// return old — the ordering is a no-op. min/max pick signed vs
// unsigned compare from the value type.
.atomic_rmw => |a| {
const table = try self.requireTable();
const vty = if (a.val_ty != .void) a.val_ty else ins.ty;
const old = try self.readField(table, frame.get(a.ptr.index()), ins.ty);
const operand = frame.get(a.operand.index());
const new_val: Reg = switch (a.kind) {
.add => old +% operand,
.sub => old -% operand,
.@"and" => old & operand,
.@"or" => old | operand,
.xor => old ^ operand,
.min, .max => blk: {
const want_max = a.kind == .max;
if (table.isUnsignedInt(vty)) {
const uo: u64 = @bitCast(old);
const up: u64 = @bitCast(operand);
break :blk @bitCast(if (want_max) @max(uo, up) else @min(uo, up));
}
break :blk if (want_max) @max(old, operand) else @min(old, operand);
},
};
try self.writeField(table, frame.get(a.ptr.index()), vty, new_val);
return .{ .value = old };
},
.struct_init => |agg| {
const table = try self.requireTable();
const sty = ins.ty;