atomics A.1b: real RMW emission (atomicrmw) + unit test (green)

emitAtomicRmw: LLVMBuildAtomicRMW (binop from RmwKind; signed Min/Max vs
unsigned UMin/UMax from val_ty; singleThread=0; LLVM supplies ABI alignment).
examples/1701 green (add/sub/and/or/xor/min/max return old values, results
verified). Unit test 'emit: atomic rmw (add + signed/unsigned min)' locks
'atomicrmw add' + signed 'min' vs unsigned 'umin'. Suite green (716/0).
This commit is contained in:
agra
2026-06-20 10:19:44 +03:00
parent 718f27e27f
commit 05311646aa
6 changed files with 98 additions and 51 deletions

View File

@@ -4,24 +4,24 @@ Companion to [PLAN-ATOMICS.md](PLAN-ATOMICS.md). Update after every step (one st
time, per the cadence rule). New corpus category: `17xx`.
## Last completed step
**A.0c (guard hardening) — DONE.** Adversarial review of A.0 found two CRITICAL silent-wrong
defects (raw LLVM verifier errors leaking via the public intrinsics) + a latent align
fallback; all fixed:
- **Scalar-kind guard** (call.zig): the size-only check admitted same-sized aggregates
(`[8]u8`, 8-byte struct) → invalid `load atomic [8 x i8]`. Now an allowlist switch
(integer/float/bool/pointer/enum/vector) rejects non-scalars loudly.
- **Per-op ordering validity** (call.zig): a load can't `release`/`acq_rel`, a store can't
`acquire`/`acq_rel` (LLVM rejects). Now a loud diagnostic, not invalid IR.
- **`val_ty` align fallback** (ops.zig): the `else .i64` (align-8) default would over-align a
sub-8 store → now bails loudly if `val_ty` is missing (a compiler bug, not a guess).
- Locked by `examples/1130-diagnostics-atomic-nonscalar.sx` +
`1131-diagnostics-atomic-bad-ordering.sx`. Suite green (713, 0 failed).
**A.1 (RMW) — DONE** (A.1a lock + A.1b green). `fetch_add/sub/and/or/xor` + `fetch_min/max`
→ LLVM `atomicrmw` (returns OLD value). New IR op `atomic_rmw` + `RmwKind` (no `nand`);
`LLVMBuildAtomicRMW` with binop from kind, signed/unsigned `Min/Max` from `val_ty`. RMW
restricted to INTEGER T (float fadd / pointer RMW out of scope, rejected loudly); all five
orderings valid for RMW. comptime_vm does real single-thread RMW. `examples/1701` (add/sub/
and/or/xor/min/max) green; unit test locks `atomicrmw add` + signed `min` vs unsigned `umin`.
Suite green (716/0).
**Capability landed (worker, commit 3c4305f):** comptime enum value params — `$o: Ordering`
now binds the variant tag and resolves in the body; during LOWERING the tag is readable via
`self.comptimeIntNamed("<param>")``?i64`. This unblocks A.0.5 (full ordering surface).
Worker limitation: `.tagged_union` constraints were skipped — user wants this lifted (any
value should be comptime-able); separate generalization worker spawned.
## Next step
**A.2 — CAS**: `compare_exchange`/`_weak` → LLVM `cmpxchg` (returns **`?T`, null = success**).
New IR op `atomic_cmpxchg` (ptr, cmp, new, success_ord, failure_ord, weak). Validate the two
orderings (failure may not be release/acq_rel nor stronger than success). `_weak``LLVMSetWeak`.
### Earlier — A.0c (guard hardening)
Adversarial review of A.0 found two CRITICAL silent-wrong defects (raw LLVM verifier errors
via the public intrinsics) + a latent align fallback; all fixed: scalar-kind allowlist +
per-op ordering validity (call.zig), `val_ty` align bail (ops.zig). Locked by examples
1130/1131. Suite green (713/0).
### Earlier — A.0b (green)
Real atomic load/store emission: `LLVMBuildLoad2`/`LLVMBuildStore`
@@ -46,19 +46,6 @@ Full atomic load/store plumbing with LLVM emission deliberately bailing loudly;
scalar-size guard, both loud; emit dispatch arms (emit_llvm.zig) → `emitAtomicLoad`/
`emitAtomicStore` (ops.zig) currently BAIL via `comptime_failed`.
## Current state
- A.0a committed; suite green.
- The recognizer + IR + emit already handle ALL FIVE orderings; only the surface bakes a
`.seq_cst` literal (the methods can't yet forward a runtime/comptime ordering — gap below).
- emit bodies are the ONLY placeholder; A.0b swaps them for real builders.
## Next step
**A.0b** — replace the `emitAtomicLoad`/`emitAtomicStore` bail with real
`LLVMBuildLoad2`+`SetOrdering`+`SetAlignment` / `LLVMBuildStore`+`SetOrdering`+
`SetAlignment` (explicit sx-tag→LLVM ordering switch); regen 1700 → green (7/42/43) + host
`.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 — 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 …
@@ -76,12 +63,14 @@ sidesteps it cleanly by requiring explicit ordering (matches the design). Candid
follow-up, not an atomics blocker.
## Known issues / capability gaps
- **Comptime-constant ordering propagation MISSING (blocks the full surface).** A runtime
`Ordering` method param can't reach LLVM (orderings are instruction attributes, not
operands), and comptime enum value params don't exist (`$o: Ordering``o` unresolved in
body; `resolveValueParamArg` folds integers only). So A.0 ships seq_cst-only; A.0.5 closes
the gap (worker: implement comptime enum value params) and MIGRATES the methods — NO
legacy left by stream end.
- **RESOLVED:** comptime-constant ordering propagation — landed via comptime value params
(3c4305f / d7a6857 / d95ba0a); A.0.5 migrated the methods, no seq_cst-only legacy.
- **Orthogonal gap (not an atomics blocker):** default VALUES for comptime params don't bind
on generic-struct methods (free-fn defaults DO work). Atomics requires explicit ordering
(design-aligned), so it's unaffected. Candidate future fix.
- **Cosmetic:** an invalid ordering passed through a method (`a.load(.release)`) reports the
diagnostic at the lib forward site (`atomic.sx`), not the user's call. Loud + correct, but
the span could be improved by threading the call-site span. Polish.
- **Latent (observed, not yet filed):** calling an *unrecognized* bodiless `#builtin`
silently returns 0 / no-ops with exit 0 (that's how 1700 behaved before recognition
landed) — a silent-fallback footgun in the generic builtin-call path, independent of
@@ -118,3 +107,6 @@ follow-up, not an atomics blocker.
(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).
- **A.1** — RMW: atomic_rmw op + RmwKind + recognizer (rmwKindFromName, integer-only) +
7 fetch_* methods/intrinsics. A.1a bail-lock → A.1b real LLVMBuildAtomicRMW (signed|unsigned
min/max). comptime_vm real RMW. 1701 + unit test. Suite green (716/0).