diff --git a/current/CHECKPOINT-FFI.md b/current/CHECKPOINT-FFI.md index cc2960d..9358b00 100644 --- a/current/CHECKPOINT-FFI.md +++ b/current/CHECKPOINT-FFI.md @@ -6,6 +6,63 @@ add a test and make it pass — that's two commits). ## Last completed step +**M5.A.next.2b — per-call-shape monomorphisation for pack-fns** +(commit `7989618`). + +Pack-fns (detected by `isPackFn(fd)` — last param is the only +comptime param AND is variadic) now emit ONE shared mono per +unique call-site signature. Repeat calls with the same +arg-type tuple share the mono; distinct shapes get distinct +symbols. Pre-2b each call inlined a fresh body copy into the +caller's basic block. + +`examples/158-pack-mono-dedup.sx` confirms end-to-end: +`count(), count(1), count(2), count(1,2,3), count("x", true)` +produces `0 1 1 3 2` at runtime AND emits exactly 4 monos in +IR — the two `s64` calls share one mono. + +Plumbing in `src/ir/lower.zig`: +- `isPackFn(fd)` — true when the only comptime param is a + trailing pack. Mixed `($fmt, ..$args)` shapes stay on the + inline `lowerComptimeCall` path (different substitution + mechanism for the non-pack comptime; deferred). +- `lowerPackFnCall` — builds mangled name + `__pack__`, cache-checks + `lowered_functions`, calls `monomorphizePackFn` on miss, + emits a direct call. +- `monomorphizePackFn` — mirrors `monomorphizeFunction`'s + save/restore + param/scope setup, with N synthesised pack + params (`__pack__`) and AST-ident substitution + installed via `pack_arg_nodes`. `pack_param_count` makes + `args.len` resolve to the comptime N via new intercepts in + `lowerFieldAccess` + `inferExprType`. `inline_return_target` + is nulled out so the mono body emits real `ret X` instead + of the inline-slot routing — it's a real fn now. +- Routed at three call sites: each `hasComptimeParams(fd) + → lowerComptimeCall` now first checks `isPackFn` and routes + to `lowerPackFnCall` when true. + +Lifetime gotcha caught and fixed: `Function.init` stores +`params.items` by reference (no copy). The local +`ArrayList(Function.Param)` must NOT be deinit'd — matches the +leak convention in `monomorphizeFunction`. Symptom of getting +this wrong: `0xAAAAAAAA` poison-pattern TypeIds in +`coerceCallArgs`. + +`examples/156-pack-typed-index.sx` (typed indexing) and +`examples/157-pack-if-return.sx` (control flow) continue to +pass unchanged on the new path. + +Out of scope (deferred to follow-up slices): +- Mixed `$fmt + ..$args` shapes. +- Generic `$R` return types. +- Bare `args` reference (passing the pack-slice as a whole). +- `args[]` non-literal indexing. + +197/197 example tests + `zig build test` green. + +--- + **M5.A.next.2a.D — inline-return uses CFG terminator, not block_terminated** (commit `e6d6903`, lock-in `6b7a66b`). @@ -526,11 +583,12 @@ plus 2 codegen fixes surfaced along the way.** ## Current state -- 196/196 example tests pass; `zig build test` green. +- 197/197 example tests pass; `zig build test` green. - Phase 3.0/3.1/3.2 + M1.0–M1.3 + M2.1–M2.3 + M3 + M4.0 + M4.A all landed. - Pack feature step 1 done (1c.A → 1d.B; commits bb6eca6 → 08feb60). -- Pack feature step 2a done — typed `args[$i]` at literal indices - (cd36784) + control-flow CFG terminator fix (e6d6903). +- Pack feature step 2 done — typed `args[$i]` at literal indices + (cd36784) + CFG terminator fix (e6d6903) + per-call-shape + mono (7989618). - issue-0045 (comptime-fn-with-return verifier crash) fixed (9e78790). - issue-0046 (nested-comptime-call + return) FILED, not blocking next slices since builders run inside `#insert`, not inside @@ -539,21 +597,16 @@ plus 2 codegen fixes surfaced along the way.** ## Pack feature — next slice options -Step 2 ("Runtime indexing + mono expansion") is functionally -sufficient for the impl-matching payoff (step 5) thanks to step -1d.B's per-mono mangling on `.convert__` keys. -Remaining within step 2 is real per-call-shape monomorphisation -(today pack-fns inline at every call site) — performance/IR-size -concern, not correctness. Defer until a real workload demands it. +Step 2 done. Pack-fns monomorphise per call shape; typed +indexing + arity work end-to-end. -Step 3 ("Type-reflection intrinsics") is the natural next slice: +Step 3 ("Type-reflection intrinsics") is the natural next: - `$args[$i]` in TYPE positions (param types, return types, - fn-pointer types, struct fields). + fn-pointer types, struct fields). Unlocks the block-trampoline + body in stdlib's generic `Into(Block) for Closure(..$args) -> + $R` impl — the body needs `typed_fn : (*void, $args[0], + $args[1]) -> $R = xx ...` per-position type interpolation. - `type_name`, `type_eq`, `has_impl` comptime intrinsics. -- Unlocks the block-trampoline body in stdlib's generic - `Into(Block) for Closure(..$args) -> $R` impl — the body - needs `typed_fn : (*void, $args[0], $args[1]) -> $R = xx ...` - per-position type interpolation. Step 4 (`#insert` pack passthrough + `compile_error`) is a parser/interp tweak letting builders raise build-time @@ -562,6 +615,14 @@ diagnostics from inside `#insert` bodies. Steps 5 and 6 are stdlib refactors gated on step 3's type-position substitution landing. +Pack feature follow-ups (deferred): +- Mixed `$fmt + ..$args` shapes through the mono path. +- Generic `$R` return type binding. +- Bare `args` reference (materialise an `[]Any` slice on demand + inside the mono). +- `args[]` non-literal indexing. +- issue-0046 fix (nested-comptime-call + return). + **M4.0 — context.allocator threading** (4 commits this session): - `__sx_allocator: Allocator` prepended at field index 0 of every sx-defined class's state struct