Files
sx/current/CHECKPOINT-ATOMICS.md
agra 79895be401 atomics A.2b: real CAS emission (cmpxchg) + unit test (green)
emitAtomicCmpxchg: LLVMBuildAtomicCmpXchg (success/failure orderings,
singleThread=0) returns a {T, i1} pair; LLVMSetWeak for the weak variant. The
sx ?T result (null = SUCCESS) is built as { extractvalue 0 (actual value),
xor(extractvalue 1 (success), true) } -- has_value = NOT success. Integer-only
(recognizer guard), so never a pointer/niche optional.

examples/1702 green: successful CAS returns null (value updated), failing CAS
returns the actual value (unchanged), weak retry loop increments a counter
(100 -> 105). LLVM IR shows `cmpxchg ... acq_rel acquire` and `cmpxchg weak`.
Unit test `emit: atomic cmpxchg (strong + weak)` locks `cmpxchg` + the weak
marker. Suite green (718/0).
2026-06-20 10:57:01 +03:00

127 lines
8.8 KiB
Markdown

# CHECKPOINT-ATOMICS — Stream A (atomics lowering)
Companion to [PLAN-ATOMICS.md](PLAN-ATOMICS.md). Update after every step (one step at a
time, per the cadence rule). New corpus category: `17xx`.
## Last completed step
**A.2 (CAS) — DONE** (A.2a lock + A.2b green). `compare_exchange`/`_weak` → LLVM `cmpxchg`
(result **`?T`, null = SUCCESS**; failure carries the actual value for retry). New IR op
`atomic_cmpxchg` + `AtomicCmpxchg{ptr, cmp, new, val_ty, success_ordering, failure_ordering,
weak}`. `emitAtomicCmpxchg`: `LLVMBuildAtomicCmpXchg` (success/failure orderings, singleThread=0)
`{T, i1}` pair; `LLVMSetWeak` for weak; `?T` result = `{ extractvalue 0 (actual),
xor(extractvalue 1, true) }` (has_value = NOT success). comptime_vm arm does real single-thread
CAS (read/compare/store-on-equal, build `?T`; weak == strong at comptime). Recognizer
(`atomic_cmpxchg`/`_weak`, 6 args) — CAS restricted to INTEGER T; BOTH orderings via
`atomicOrderingFromNode`; dual-ordering validation (failure may not be release/acq_rel nor
stronger than success, `atomicOrderingRank`). Methods `compare_exchange`/`_weak` on `Atomic($T)`
with comptime `$success`/`$failure: Ordering`. `examples/1702` green (CAS ok→20 / fail actual=20 /
weak retry loop 100→105); `examples/1186` locks a rejected ordering pair; unit test `emit: atomic
cmpxchg (strong + weak)` asserts `cmpxchg` + `cmpxchg weak`. Suite green (718/0).
### 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` green; unit test locks `atomicrmw add` + signed `min` vs unsigned `umin`.
## Next step
**A.3 — fence** (`atomic_fence($o: Ordering)` → LLVM `fence`), per PLAN-ATOMICS. No value
result; ordering must be acquire/release/acq_rel/seq_cst (relaxed is meaningless for a fence —
reject loudly). New IR op `atomic_fence` + dispatch/print/comptime_vm (no-op single-thread) +
`LLVMBuildFence`. Lock-then-green cadence as before.
### 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`
+ `LLVMSetOrdering` + mandatory `LLVMSetAlignment`, ordering via an explicit
sx-tag→`LLVMAtomicOrdering` switch (`llvmOrdering`). `examples/1700` green (7/42/43); IR
shows `load atomic i64, ptr … seq_cst, align 8` + `store atomic …`. Added unit test
`emit: atomic load/store (seq_cst, aligned)` in `emit_llvm.test.zig` (asserts `load
atomic`/`store atomic`/`seq_cst`/`align 8`). No fragile full-module `.ir` snapshot for 1700
(it uses `print`); the unit test is the emission-shape gate. Suite green (710 + units).
### Earlier — A.0a (lock commit)
Full atomic load/store plumbing with LLVM emission deliberately bailing loudly;
`examples/1700` locked to the bail diagnostic.
- `library/modules/std/atomic.sx`: `Ordering` enum, `Atomic($T)` struct (`init`/`load`/
`store`, **seq_cst-only** — see capability gap below), `atomic_load`/`atomic_store`
`#builtin` decls. **Opt-in import**, NOT in the universal `std.sx` facade (mirrors
`trace`) — putting `Ordering` in the prelude grew every program's type table 378→380 and
churned 37 `.ir` snapshots; reverted.
- IR ops `atomic_load`/`atomic_store` + `AtomicOrdering` (all 5) + structs (inst.zig);
print arms (print.zig); comptime_vm arms reuse load/store (single-thread correct);
recognizer `tryLowerAtomicIntrinsic` (call.zig) — const-ordering-literal guard +
scalar-size guard, both loud; emit dispatch arms (emit_llvm.zig) → `emitAtomicLoad`/
`emitAtomicStore` (ops.zig) currently BAIL via `comptime_failed`.
## 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
- **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
atomics. Flag to user; candidate `issues/` entry.
## Decisions (Stream A specifics; surface locked in design §4.6)
- `Atomic($T)` = pure-sx transparent 1-field struct (NO new IR type); ops = `#builtin`
intrinsics emitted as new IR ops. Minimal compiler surface.
- Ordering is compile-time-only (const enum literal), baked into the op as a Zig enum;
non-literal = loud diagnostic. sx tag → LLVM ordering via explicit switch (LLVM enum is
non-contiguous: 2/4/5/6/7).
- Atomic load/store REQUIRE explicit alignment (`LLVMSetAlignment`) — verifier mandate.
- Comptime VM treats atomics as ordinary load/store (single-thread ⇒ correct), not a bail.
- **Snapshot scope corrected:** `.ir` (LLVM IR) is arch-invariant for atomics → ONE host
`.ir` per op, not arch-gated x86/aarch64 pairs (they'd be byte-identical). Asm-level arch
divergence + weak-memory semantics are OUT of corpus scope (stress harness, Stream C).
## Log
- **carve** — wrote PLAN-ATOMICS.md + CHECKPOINT-ATOMICS.md; grounded the intrinsic path,
switch sites, LLVM-C API (no `LLVMBuildAtomicLoad`; use `LLVMBuildLoad2`+`SetOrdering`+
`SetAlignment`), and corrected the arch-`.ir` misconception (`sx ir` emits arch-invariant
LLVM IR). Stream ready; A.0a is the first implementation step.
- **A.0a** — landed lib (atomic.sx, opt-in import) + IR ops (atomic_load/atomic_store +
AtomicOrdering) + recognizer + print/vm arms + emit BAIL; locked `examples/1700` to the
bail diagnostic. Reverted a universal-facade wiring that churned 37 `.ir` snapshots
(Ordering would bloat every program's type table). Suite green (710/0).
- **A.0b** — real atomic load/store emission (LLVMBuildLoad2/Store + SetOrdering +
SetAlignment; explicit sx→LLVM ordering switch). 1700 green (7/42/43, `load atomic …
seq_cst, align 8`). Unit test added. Suite green (710 + units).
- **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
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).
- **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).