atomics A.0.5: full ordering surface (comptime $o: Ordering)
Migrate Atomic methods from seq_cst-only to the explicit ordering surface now that comptime value params work on generic-struct methods (workers3c4305f/d7a6857/d95ba0a): - atomic.sx: load/store take a comptime $o: Ordering (explicit, Rust-style; no default, matching design 4.6). a.load(.acquire) -> 'load atomic .. acquire'. - call.zig: atomicOrderingFromNode resolves a comptime-bound ordering identifier via comptimeIntNamed (+ atomicOrderingFromTag); documents the sx-Ordering <-> IR-AtomicOrdering declaration-order invariant. The per-op validity guard fires through the method path (a.load(.release) is a compile error). - 1700 migrated to explicit orderings (output unchanged 7/42/43). Suite green (715/0).
This commit is contained in:
@@ -59,6 +59,22 @@ Full atomic load/store plumbing with LLVM emission deliberately bailing loudly;
|
|||||||
`.ir`; add `emit_llvm.test.zig` unit. Then adversarial review, then the comptime-enum worker
|
`.ir`; add `emit_llvm.test.zig` unit. Then adversarial review, then the comptime-enum worker
|
||||||
+ A.0.5 migration to the full ordering surface.
|
+ A.0.5 migration to the full ordering surface.
|
||||||
|
|
||||||
|
## A.0.5 — full ordering surface (DONE)
|
||||||
|
`Atomic($T).load($o: Ordering)` / `store(v, $o)` — ordering is a COMPTIME value param,
|
||||||
|
explicit (Rust-style, no default; design §4.6). `a.load(.acquire)` emits `load atomic …
|
||||||
|
acquire`; `a.store(v, .release)` emits `store atomic … release`; `a.load(.release)` is a
|
||||||
|
compile error (per-op validity guard fires through the method path). Recognizer
|
||||||
|
`atomicOrderingFromNode` now resolves a comptime-bound ordering identifier via
|
||||||
|
`comptimeIntNamed` (+ `atomicOrderingFromTag`, with the sx-Ordering ↔ IR-AtomicOrdering
|
||||||
|
declaration-order invariant documented). 1700 migrated to explicit orderings (output
|
||||||
|
unchanged 7/42/43). Suite green (715/0).
|
||||||
|
|
||||||
|
**Unblocked by three comptime-value-param commits (workers):** enum (3c4305f), tagged_union
|
||||||
|
(d7a6857), generic-struct methods (d95ba0a). NOTE: default VALUES for comptime params on
|
||||||
|
generic-struct methods are NOT bound (orthogonal gap — free-fn defaults work); atomics
|
||||||
|
sidesteps it cleanly by requiring explicit ordering (matches the design). Candidate
|
||||||
|
follow-up, not an atomics blocker.
|
||||||
|
|
||||||
## Known issues / capability gaps
|
## Known issues / capability gaps
|
||||||
- **Comptime-constant ordering propagation MISSING (blocks the full surface).** A runtime
|
- **Comptime-constant ordering propagation MISSING (blocks the full surface).** A runtime
|
||||||
`Ordering` method param can't reach LLVM (orderings are instruction attributes, not
|
`Ordering` method param can't reach LLVM (orderings are instruction attributes, not
|
||||||
@@ -98,3 +114,7 @@ Full atomic load/store plumbing with LLVM emission deliberately bailing loudly;
|
|||||||
- **A.0c** — guard hardening from the adversarial review: scalar-kind allowlist + per-op
|
- **A.0c** — guard hardening from the adversarial review: scalar-kind allowlist + per-op
|
||||||
ordering validity (call.zig), val_ty align bail (ops.zig), + diagnostic examples
|
ordering validity (call.zig), val_ty align bail (ops.zig), + diagnostic examples
|
||||||
1130/1131. Suite green (713/0). (comptime enum value params landed via worker 3c4305f.)
|
1130/1131. Suite green (713/0). (comptime enum value params landed via worker 3c4305f.)
|
||||||
|
- **A.0.5** — full ordering surface: `Atomic($T).load/store($o: Ordering)` comptime ordering
|
||||||
|
(explicit). Recognizer resolves comptime-bound ordering via `comptimeIntNamed`. 1700
|
||||||
|
migrated to explicit orderings (acquire/release/relaxed/seq_cst). Unblocked by
|
||||||
|
comptime-value-param workers (3c4305f/d7a6857/d95ba0a). Suite green (715/0).
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
// Atomic($T) load/store (seq_cst), single-thread.
|
// Atomic($T) load/store with explicit memory orderings, single-thread.
|
||||||
// Stream A (atomics) A.0 — the first net-new atomic codegen example.
|
// Stream A (atomics) A.0 + A.0.5 — the ordering is a comptime `$o: Ordering`
|
||||||
// Explicit orderings (a.load(.acquire)) arrive in A.0.5; see PLAN-ATOMICS.md.
|
// param (explicit, Rust-style): a.load(.acquire) emits `load atomic … acquire`.
|
||||||
// Atomics is an opt-in import (not in the universal prelude) — like `trace`.
|
// An invalid combination (a.load(.release)) is a compile error (see 1131).
|
||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
#import "modules/std/atomic.sx";
|
#import "modules/std/atomic.sx";
|
||||||
|
|
||||||
main :: () {
|
main :: () {
|
||||||
a := Atomic(i64).init(7);
|
a := Atomic(i64).init(7);
|
||||||
print("init: {}\n", a.load());
|
print("init: {}\n", a.load(.seq_cst));
|
||||||
|
|
||||||
a.store(42);
|
a.store(42, .release);
|
||||||
print("after store: {}\n", a.load());
|
print("after store: {}\n", a.load(.acquire));
|
||||||
|
|
||||||
a.store(a.load() + 1);
|
a.store(a.load(.relaxed) + 1, .seq_cst);
|
||||||
print("incremented: {}\n", a.load());
|
print("incremented: {}\n", a.load(.seq_cst));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,11 +23,11 @@ Ordering :: enum {
|
|||||||
atomic_load :: ($T: Type, ptr: *T, o: Ordering) -> T #builtin;
|
atomic_load :: ($T: Type, ptr: *T, o: Ordering) -> T #builtin;
|
||||||
atomic_store :: ($T: Type, ptr: *T, v: T, o: Ordering) #builtin;
|
atomic_store :: ($T: Type, ptr: *T, v: T, o: Ordering) #builtin;
|
||||||
|
|
||||||
// NOTE (A.0): the methods bake a literal `.seq_cst` (strongest, conservative)
|
// The ordering is a COMPTIME value param (`$o`): it must be known at compile
|
||||||
// rather than taking an `o: Ordering` parameter. A runtime ordering param can't
|
// time because LLVM atomic ordering is an instruction attribute, not a runtime
|
||||||
// reach the intrinsic as the compile-time constant LLVM requires, and comptime
|
// operand. It is explicit (Rust-style — no default), so the caller always states
|
||||||
// enum value params don't exist yet — so explicit orderings (`a.load(.acquire)`)
|
// the ordering: `a.load(.acquire)`, `a.store(v, .release)`. An invalid
|
||||||
// land in A.0.5 once that capability does. See current/PLAN-ATOMICS.md.
|
// combination (`a.load(.release)`) is a compile error.
|
||||||
Atomic :: struct ($T: Type) {
|
Atomic :: struct ($T: Type) {
|
||||||
value: T;
|
value: T;
|
||||||
|
|
||||||
@@ -35,11 +35,11 @@ Atomic :: struct ($T: Type) {
|
|||||||
return .{ value = v };
|
return .{ value = v };
|
||||||
}
|
}
|
||||||
|
|
||||||
load :: (self: *Atomic(T)) -> T {
|
load :: (self: *Atomic(T), $o: Ordering) -> T {
|
||||||
return atomic_load(T, @self.value, .seq_cst);
|
return atomic_load(T, @self.value, o);
|
||||||
}
|
}
|
||||||
|
|
||||||
store :: (self: *Atomic(T), v: T) {
|
store :: (self: *Atomic(T), v: T, $o: Ordering) {
|
||||||
atomic_store(T, @self.value, v, .seq_cst);
|
atomic_store(T, @self.value, v, o);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1678,12 +1678,28 @@ pub fn lowerRuntimeDispatchCall(
|
|||||||
return self.builder.constInt(0, .void);
|
return self.builder.constInt(0, .void);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Map a bare ordering enum literal (`.seq_cst`) to the IR `AtomicOrdering`.
|
/// The five `Ordering` variants by declaration-order tag. INVARIANT: the sx
|
||||||
/// Returns null for anything that is not one of the five constant literals —
|
/// `Ordering` enum (library/modules/std/atomic.sx) and the IR `AtomicOrdering`
|
||||||
/// the caller turns that into a loud "must be a constant ordering literal"
|
/// enum (inst.zig) declare these variants in the SAME order, so a comptime-bound
|
||||||
/// diagnostic (never a silent default).
|
/// ordering's tag indexes this list. Keep all three in sync.
|
||||||
fn atomicOrderingFromNode(node: *const Node) ?inst_mod.AtomicOrdering {
|
fn atomicOrderingFromTag(tag: i64) ?inst_mod.AtomicOrdering {
|
||||||
if (node.data != .enum_literal) return null;
|
return switch (tag) {
|
||||||
|
0 => .relaxed,
|
||||||
|
1 => .acquire,
|
||||||
|
2 => .release,
|
||||||
|
3 => .acq_rel,
|
||||||
|
4 => .seq_cst,
|
||||||
|
else => null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolve an ordering argument to the IR `AtomicOrdering`. Accepts a bare enum
|
||||||
|
/// literal (`.seq_cst`) OR a comptime-bound identifier (a `$o: Ordering` param
|
||||||
|
/// forwarded into the intrinsic — read its bound variant tag via
|
||||||
|
/// `comptimeIntNamed`). Returns null for a non-constant ordering — the caller
|
||||||
|
/// turns that into a loud diagnostic (never a silent default).
|
||||||
|
fn atomicOrderingFromNode(self: *Lowering, node: *const Node) ?inst_mod.AtomicOrdering {
|
||||||
|
if (node.data == .enum_literal) {
|
||||||
const n = node.data.enum_literal.name;
|
const n = node.data.enum_literal.name;
|
||||||
if (std.mem.eql(u8, n, "relaxed")) return .relaxed;
|
if (std.mem.eql(u8, n, "relaxed")) return .relaxed;
|
||||||
if (std.mem.eql(u8, n, "acquire")) return .acquire;
|
if (std.mem.eql(u8, n, "acquire")) return .acquire;
|
||||||
@@ -1691,6 +1707,11 @@ fn atomicOrderingFromNode(node: *const Node) ?inst_mod.AtomicOrdering {
|
|||||||
if (std.mem.eql(u8, n, "acq_rel")) return .acq_rel;
|
if (std.mem.eql(u8, n, "acq_rel")) return .acq_rel;
|
||||||
if (std.mem.eql(u8, n, "seq_cst")) return .seq_cst;
|
if (std.mem.eql(u8, n, "seq_cst")) return .seq_cst;
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
if (node.data == .identifier) {
|
||||||
|
if (self.comptimeIntNamed(node.data.identifier.name)) |tag| return atomicOrderingFromTag(tag);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Recognize the atomic `#builtin` intrinsics and lower them to dedicated atomic
|
/// Recognize the atomic `#builtin` intrinsics and lower them to dedicated atomic
|
||||||
@@ -1729,7 +1750,7 @@ pub fn tryLowerAtomicIntrinsic(self: *Lowering, name: []const u8, c: *const ast.
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ord_node = c.args[expected - 1];
|
const ord_node = c.args[expected - 1];
|
||||||
const ordering = atomicOrderingFromNode(ord_node) orelse {
|
const ordering = atomicOrderingFromNode(self, ord_node) orelse {
|
||||||
if (self.diagnostics) |d| d.addFmt(.err, ord_node.span, "atomic ordering must be a constant ordering literal (.relaxed / .acquire / .release / .acq_rel / .seq_cst)", .{});
|
if (self.diagnostics) |d| d.addFmt(.err, ord_node.span, "atomic ordering must be a constant ordering literal (.relaxed / .acquire / .release / .acq_rel / .seq_cst)", .{});
|
||||||
return Ref.none;
|
return Ref.none;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user