Files
sx/current/CHECKPOINT-ATOMICS.md
agra 8144a88a21 atomics A.0c: harden guards (scalar-kind, ordering validity, align bail)
Adversarial review of A.0 found two silent-wrong defects reachable via the public
atomic_load/atomic_store intrinsics (raw LLVM verifier errors, not clean sx
diagnostics) + a latent alignment fallback. All fixed:

- scalar-kind allowlist (call.zig): the size-only T guard admitted same-sized
  aggregates ([8]u8, 8-byte structs) -> invalid 'load atomic [8 x i8]'. Now an
  allowlist switch (integer/float/bool/pointer/enum/vector) rejects loudly.
- per-op ordering validity (call.zig): load cannot release/acq_rel, store cannot
  acquire/acq_rel -> loud diagnostic instead of invalid LLVM.
- val_ty align fallback (ops.zig): the 'else .i64' (align 8) default would
  over-align a sub-8 store -> now bails loudly on a missing val_ty.

Locked by examples 1130 (non-scalar) + 1131 (bad ordering). Suite green (713/0).
2026-06-20 09:26:53 +03:00

6.6 KiB

CHECKPOINT-ATOMICS — Stream A (atomics lowering)

Companion to PLAN-ATOMICS.md. Update after every step (one step at a 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).

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.

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.

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.

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: Orderingo 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.
  • 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.)