diff --git a/current/PLAN-ATOMICS.md b/current/PLAN-ATOMICS.md index c5082a8d..3f79e79b 100644 --- a/current/PLAN-ATOMICS.md +++ b/current/PLAN-ATOMICS.md @@ -1,5 +1,20 @@ # PLAN-ATOMICS — Stream A (atomics lowering) +> **STATUS: ✅ COMPLETE (feature-complete).** All phases A.0 → A.3 landed + green. +> Surface shipped: `Atomic($T)` `load`/`store`/`fetch_add`/`sub`/`and`/`or`/`xor`/`min`/`max`/ +> `swap`/`compare_exchange`/`compare_exchange_weak` (all comptime `$o: Ordering`) + free +> `fence(.ordering)`. IR ops `atomic_load`/`store`/`rmw`/`cmpxchg`/`fence`. Both LLVM emit +> AND the comptime VM implemented (verified to agree). Enabled by net-new comptime value +> params (enum/tagged_union/generic-struct methods — 3 commits). Corpus `17xx` (1700-1704) + +> `11xx` diagnostics (1130/1131/1186/1187). Commits: 22af404, 64c7db5, 8144a88, acf3183, +> 718f27e, 0531164, 68ed732, dca396e, 79895be, fca4304, b65544a (+ comptime-param 3c4305f, +> d7a6857, d95ba0a). **Unblocks Stream B2 channels + Stream C parallel schedulers.** +> +> Deferred (documented, NOT legacy — intentional scope): RMW/CAS/swap are integer-only +> (float fadd / pointer atomics out of scope); fence/orderings explicit (no defaults — the +> comptime-default-on-generic-method gap is orthogonal). Asm-level arch divergence + +> weak-memory *semantics* remain OUT of corpus scope (Stream-C stress harness). + Carved from [PLAN-POST-METATYPE.md](PLAN-POST-METATYPE.md) Stream A + the design-of-record [../design/execution-evolution-roadmap.md](../design/execution-evolution-roadmap.md) §3 (N1) + §4.6 (locked surface). Progress in [CHECKPOINT-ATOMICS.md](CHECKPOINT-ATOMICS.md). diff --git a/current/PLAN-POST-METATYPE.md b/current/PLAN-POST-METATYPE.md index 316fee85..e5fa7aaa 100644 --- a/current/PLAN-POST-METATYPE.md +++ b/current/PLAN-POST-METATYPE.md @@ -19,7 +19,7 @@ JIT/FFI cluster comes after. New corpus categories: `17xx` atomics, `18xx` concu | # | Stream | Roadmap steps | Depends on | Notes | |---|--------|---------------|-----------|-------| -| **A** | Atomics | N1 (1) | — | independent foundation; gates B-parallel + channels | +| **A** | Atomics | N1 (1) | — | ✅ **DONE** — `PLAN-ATOMICS.md`. load/store/RMW/CAS/swap/fence; comptime value params landed alongside. Gates B2-channels + C-parallel | | **B** | Async runtime | 4–12 | metatype, A (for channels) | the bulk; likely splits into B1 (runtime) + B2 (channels/cancel/stdlib) when carved | | **C** | Parallel schedulers | 13–14 | A, B | N×(M:1) → M:N | | **D** | Comptime JIT/FFI | 15–18 | — (independent of async) | S1 → C1 → C2 → C3 | @@ -31,7 +31,7 @@ earlier if FFI/`#compiler`-collapse becomes a priority). --- -## Stream A — ATOMICS (N1) · `PLAN-ATOMICS.md` when carved +## Stream A — ATOMICS (N1) · ✅ **COMPLETE** — see [PLAN-ATOMICS.md](PLAN-ATOMICS.md) **Goal:** LLVM atomic codegen — the net-new emit primitive. Surface = `Atomic($T)` wrapper + `Ordering` enum (locked, design §4.6). **Grounding correction: this is 100% diff --git a/examples/1703-atomics-swap.sx b/examples/1703-atomics-swap.sx index f87995c6..6eaeed8d 100644 --- a/examples/1703-atomics-swap.sx +++ b/examples/1703-atomics-swap.sx @@ -1,11 +1,16 @@ // Atomic($T).swap — atomic exchange (LLVM atomicrmw xchg): store the new value, // return the OLD one. Stream A (atomics) A.3. Single-thread. +// Covers swap at BOTH comptime (#run) and runtime — they must agree. #import "modules/std.sx"; #import "modules/std/atomic.sx"; +c_swap :: () -> i64 { a := Atomic(i64).init(7); old := a.swap(42, .seq_cst); return old * 100 + a.load(.seq_cst); } +G_SWAP :: #run c_swap(); // 742 (old 7, now 42) + main :: () { a := Atomic(i64).init(7); old := a.swap(42, .acq_rel); print("swap old: {}\n", old); // 7 print("swap now: {}\n", a.load(.acquire)); // 42 + print("comptime swap: {}\n", G_SWAP); // 742 (matches runtime) } diff --git a/examples/expected/1703-atomics-swap.stdout b/examples/expected/1703-atomics-swap.stdout index ec8af639..db26bece 100644 --- a/examples/expected/1703-atomics-swap.stdout +++ b/examples/expected/1703-atomics-swap.stdout @@ -1,2 +1,3 @@ swap old: 7 swap now: 42 +comptime swap: 742 diff --git a/readme.md b/readme.md index c2ceda33..28528f34 100644 --- a/readme.md +++ b/readme.md @@ -675,6 +675,36 @@ The standard library (`modules/std.sx`) provides: - **Math**: `sqrt`, `sin`, `cos` - **Introspection**: `type_of`, `type_name`, `type_is_unsigned`, `type_eq`, `field_count`, `field_name`, `field_value`, `size_of`, `align_of`, `is_flags` — the type-only builtins (`size_of`, `align_of`, `field_count`, `type_name`, `type_eq`, `type_is_unsigned`, `is_flags`) require a type argument (a spelled type or a generic `T`); passing a value is a compile-time error. A runtime `Type` value (`type_of(x)`) is currently accepted by `type_name` and `type_is_unsigned` only — the other five are compile-time-only (runtime reflection is deferred) +### Atomics (`modules/std/atomic.sx`) + +Opt-in import (not in the universal prelude). `Atomic($T)` is a transparent +wrapper over an integer/pointer-sized `T`; the memory `Ordering` is a **compile-time +value parameter** (`$o`) — LLVM atomic ordering is an instruction attribute, so it +must be known at compile time, and it's always explicit (Rust-style, no default): + +```sx +#import "modules/std/atomic.sx"; + +counter : Atomic(i64) = .init(0); +counter.store(0, .relaxed); +n := counter.load(.acquire); +prev := counter.fetch_add(1, .seq_cst); // + fetch_sub/and/or/xor/min/max +old := counter.swap(42, .acq_rel); + +// compare-exchange returns ?T — null = SUCCESS; a present value is the actual +// current value on failure (for a retry loop). `_weak` may fail spuriously. +got := counter.compare_exchange(old, 99, .acq_rel, .acquire); +if got == null { /* swapped */ } else { /* retry with got! */ } + +fence(.seq_cst); // standalone memory fence (.relaxed is rejected) +``` + +`Ordering` = `relaxed`/`acquire`/`release`/`acq_rel`/`seq_cst`. Invalid combinations +are compile errors (a load can't be `.release`; a store can't be `.acquire`; CAS's +failure ordering can't be stronger than success; a fence can't be `.relaxed`). +RMW/CAS/swap are integer-only. The same operations run at compile time (`#run`) +under single-threaded semantics, matching the runtime result. + ### Command-line interface (`modules/std/cli.sx`) `std.cli` builds command-line front-ends over an explicit logical argv