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:
@@ -1725,9 +1725,11 @@ fn atomicOrderingFromNode(self: *Lowering, node: *const Node) ?inst_mod.AtomicOr
|
||||
pub fn tryLowerAtomicIntrinsic(self: *Lowering, name: []const u8, c: *const ast.Call) ?Ref {
|
||||
const is_load = std.mem.eql(u8, name, "atomic_load");
|
||||
const is_store = std.mem.eql(u8, name, "atomic_store");
|
||||
if (!is_load and !is_store) return null;
|
||||
const rmw_kind = rmwKindFromName(name); // atomic_fetch_add/sub/and/or/xor/min/max
|
||||
if (!is_load and !is_store and rmw_kind == null) return null;
|
||||
|
||||
const expected: usize = if (is_load) 3 else 4; // ($T, ptr[, val], ordering)
|
||||
// ($T, ptr[, operand/val], ordering): load=3, store/rmw=4.
|
||||
const expected: usize = if (is_load) 3 else 4;
|
||||
if (c.args.len != expected) {
|
||||
if (self.diagnostics) |d| d.addFmt(.err, c.callee.span, "{s} expects {d} arguments", .{ name, expected });
|
||||
return Ref.none;
|
||||
@@ -1748,6 +1750,19 @@ pub fn tryLowerAtomicIntrinsic(self: *Lowering, name: []const u8, c: *const ast.
|
||||
if (self.diagnostics) |d| d.addFmt(.err, c.args[0].span, "atomic ops require a scalar type (integer/float/bool/pointer/enum/vector) of size 1/2/4/8/16 bytes — '{s}' is not eligible", .{self.formatTypeName(elem_ty)});
|
||||
return Ref.none;
|
||||
}
|
||||
// RMW (A.1) is restricted to INTEGER types: arithmetic/bitwise/min-max on
|
||||
// floats (fadd/fsub) and pointers is out of scope — reject loudly rather
|
||||
// than emit invalid LLVM.
|
||||
if (rmw_kind != null) {
|
||||
const int_ok = switch (self.module.types.get(elem_ty)) {
|
||||
.signed, .unsigned => true,
|
||||
else => false,
|
||||
};
|
||||
if (!int_ok) {
|
||||
if (self.diagnostics) |d| d.addFmt(.err, c.args[0].span, "atomic read-modify-write requires an integer type — '{s}' is not eligible", .{self.formatTypeName(elem_ty)});
|
||||
return Ref.none;
|
||||
}
|
||||
}
|
||||
|
||||
const ord_node = c.args[expected - 1];
|
||||
const ordering = atomicOrderingFromNode(self, ord_node) orelse {
|
||||
@@ -1755,7 +1770,7 @@ pub fn tryLowerAtomicIntrinsic(self: *Lowering, name: []const u8, c: *const ast.
|
||||
return Ref.none;
|
||||
};
|
||||
// Per-op ordering validity (LLVM rejects these). A load can't release; a
|
||||
// store can't acquire; neither can acq_rel. Loud diagnostic, not invalid IR.
|
||||
// store can't acquire; neither can acq_rel. (RMW accepts all orderings.)
|
||||
if (is_load and (ordering == .release or ordering == .acq_rel)) {
|
||||
if (self.diagnostics) |d| d.addFmt(.err, ord_node.span, "atomic load ordering cannot be .release or .acq_rel (use .relaxed / .acquire / .seq_cst)", .{});
|
||||
return Ref.none;
|
||||
@@ -1770,10 +1785,26 @@ pub fn tryLowerAtomicIntrinsic(self: *Lowering, name: []const u8, c: *const ast.
|
||||
return self.builder.emit(.{ .atomic_load = .{ .ptr = ptr, .ordering = ordering } }, elem_ty);
|
||||
}
|
||||
const val = self.lowerExpr(c.args[2]);
|
||||
if (rmw_kind) |kind| {
|
||||
// RMW returns the OLD value (result type = T).
|
||||
return self.builder.emit(.{ .atomic_rmw = .{ .ptr = ptr, .operand = val, .val_ty = elem_ty, .ordering = ordering, .kind = kind } }, elem_ty);
|
||||
}
|
||||
self.builder.emitVoid(.{ .atomic_store = .{ .ptr = ptr, .val = val, .val_ty = elem_ty, .ordering = ordering } }, .void);
|
||||
return Ref.none; // store has a void result
|
||||
}
|
||||
|
||||
/// Map an `atomic_fetch_*` intrinsic name to its RMW kind (null if not one).
|
||||
fn rmwKindFromName(name: []const u8) ?inst_mod.RmwKind {
|
||||
if (std.mem.eql(u8, name, "atomic_fetch_add")) return .add;
|
||||
if (std.mem.eql(u8, name, "atomic_fetch_sub")) return .sub;
|
||||
if (std.mem.eql(u8, name, "atomic_fetch_and")) return .@"and";
|
||||
if (std.mem.eql(u8, name, "atomic_fetch_or")) return .@"or";
|
||||
if (std.mem.eql(u8, name, "atomic_fetch_xor")) return .xor;
|
||||
if (std.mem.eql(u8, name, "atomic_fetch_min")) return .min;
|
||||
if (std.mem.eql(u8, name, "atomic_fetch_max")) return .max;
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Try to lower a call as a reflection builtin (expanded inline during lowering).
|
||||
/// Returns null if the call is not a recognized reflection builtin.
|
||||
pub fn tryLowerReflectionCall(self: *Lowering, name: []const u8, c: *const ast.Call) ?Ref {
|
||||
|
||||
Reference in New Issue
Block a user