Archive — history, not state. Kept for its reasoning and its evidence; its plan is closed.

Spectralnorm transient raw inline chain

Status: retry rejected; compiler implementation deleted. The QBE-only proof self-hosted and passed its focused tests, but the required boundary oracle disrupted in Mach before the native lane ran. There is no retry timing result and no performance claim. Historical first-attempt IL measurements below are design evidence only, not validation of this retry.

The retry also converts aot_bench/dumpil.ce from its environment-only bare script interface to a typed one-shot command:

./pit aot_bench/dumpil.ce <file> [raw|mcode|il|asm]

The descriptor publishes a 600-second reply deadline, the actor replies only after the dump is complete, and then stops. This tooling-only change is the sole retained code from the retry; it does not affect language output.

Hypothesis

After structured loop phis and dead inlined-callee cleanup, each spectralnorm row keeps its accumulator in a QBE double and its index in a QBE integer. The hottest path nevertheless crosses the tagged PitValue representation twice per inner iteration:

divide -> qbe_new_float64 -> inlined return move
       -> raw array load -> multiply -> qbe_new_float64 -> raw accumulator add

That is 12.8 million materializations in the benchmark’s 6.4 million inner iterations. qbe_new_float64 does not allocate, but it is still an out-of-line classification/encoding call followed by a later decode. This experiment keeps the two intermediate arithmetic values in QBE double temporaries instead.

Candidate lowering

The QBE emitter recognizes only the exact final mcode region produced by the current inliner and numeric/array guards:

divide
<private inlined-success label>
move return, divide-result
jump <private continuation>
<zero-reference empty inlined-bad label>
<continuation>
load array element
jump <load join>
<zero-reference null alternate>: null
<load join>: is_num; jump_false <numeric error>
multiply product, return, element
jump <product join>
<numeric error>: ... call panic ... disrupt
<product join>
add raw-accumulator, raw-accumulator, product

The selected divide, move, and multiply do not write an intermediate PitValue. Both arithmetic results still go through canonical_double, exactly as every other raw arithmetic boundary does. The array helper retains its ordinary frame write for the loaded element and returns the same value in the raw lane. The result is therefore a compiler temporary only: no Pit frame slot, heap object, persistent metadata, actor state, or GC root is added.

The empty inlined-bad label is omitted only after proving it has no reference and is immediately followed by the continuation. This prevents the QBE text walker from manufacturing an unreachable syntactic predecessor which lacks the raw return definition.

Rejection proof

The recognizer declines unless all of these facts hold:

  • nan64 raw-double lowering and a pre-existing raw numeric loop lane are active;
  • the function has no disruption handler;
  • receiver and index are point-proven array and integer at the exact load;
  • every success, continuation, load, error, and product label has the exact reference count expected by the compiler-private shape;
  • the inline alternate and load-null labels have no external reference;
  • the divide, return, and product slots are distinct, unpromoted, and not captured by any nested closure;
  • the divide value is dead after the load overwrites its slot, the return value is dead after multiplication, and the product is dead after the accumulator add on every successor reachable after its definition;
  • the guarded cold path ends in disrupt before the product join.

The numeric-error edge branches before the product instruction and therefore has no raw product value to preserve. Its physical frame slot retains an ordinary tagged value, while the unique cold edge terminates in disrupt. Applying post-definition product liveness to that pre-definition edge is both unnecessary and overly conservative: the generic liveness walker deliberately stops at the diagnostic call because call operands are variable-shaped.

There is no semantic call, invoke, resume, or suspension point while the raw value is live. The exact array helper is a nonallocating C/QBE helper which cannot suspend an actor; it receives no raw value and returns before the raw return temporary is consumed. The cold diagnostic call is on the branch where the product temporary has not been defined and terminates in disruption.

Proof regressions cover the positive spectral-like region, a second edge into the inline continuation, and a child closure capturing the return cell. A runtime oracle covers finite results, division infinity, NaN, nan64 range overflow, signed zero, and the array-element disruption edge. Expected Mach and native result is 63.

Acceptance gate

This retry is based on exact language-performance baseline 9313e4ee; its historical first attempt was based on the earlier dead-callee cleanup. Before acceptance it must show:

  1. focused compiler and numeric-boundary regressions pass;
  2. spectral mcode is byte-identical to 9313e4ee (QBE-only change);
  3. exactly the two intended row regions select, with both hot qbe_new_float64 materializations absent;
  4. ARM64 assembly and native code size do not grow enough to outweigh a stable runtime gain;
  5. alternating isolated spectralnorm A/B runs preserve checksum 1,623,647,009 and produce a repeatable native improvement;
  6. the default suite and deterministic compiler fuzz remain green.

If inline canonicalization merely replaces two helper calls with more branch or register-pressure cost and does not win repeatedly, the lowering should be deleted. Compilation time and compiler-memory cost are not acceptance criteria for this campaign; target code, target memory, correctness, and runtime are.

First-attempt results

The first self-host attempt exposed a real compiler-size constraint. Adding three label-walk scratch locals made compile_fn require 255 Mach slots, then Mach lowering needed its mandatory 256th scratch and rejected the module. The top-level command reported only mach compile/cache failed; the actual requires 256 register slots diagnostic was present only in .pit/log/observe.jsonl. Reusing existing emitter scratch bindings restored the self-host margin. Focused compiler tests then passed 115/115, including the positive region and both rejection tests.

The spectralnorm emitted-IL audit selected exactly two regions, one in each row function. It confirmed:

  • _inl1_stone_escape_done_1 and its sibling were zero-reference, operationless generated labels with no source origin, and were absent from candidate QBE;
  • return_raw is defined before the array-load diamond and therefore dominates both paths into load_done;
  • the only alternate to the product-success edge ends in the original panic call and disrupt/return, so it does not reach multiply_num_done_*_end;
  • diagnostic operations and their original mcode line/column operands were unchanged because the experiment modified only QBE emission;
  • candidate IL was 173,730 bytes versus 176,499 bytes at ae3fd976, a reduction of 2,769 bytes (1.57%).

The boundary fixture returned 63 in Mach and exercised the intended diagnostic origin for the null-element edge. Its native compile reached QBE successfully (163,676 bytes IL to 99,960 bytes assembly), but the in-process node stopped the benchmark actor immediately afterward without completing the async native build. A fresh daemon-mode attempt then remained alive without a listener for the full 120-second readiness timeout. Two earlier successful commands also left a live PID and socket whose endpoint refused the very next command; each was recovered by killing only the exact worktree PID.

No native correctness result or spectralnorm timing was therefore obtained. The compiler lowering, proof tests, benchmark registration, and oracle were deleted. The result remains useful evidence: this exact raw chain reduces IL and has a credible CFG proof, but it must be retried only after native builds can run deterministically in an isolated worktree. The retry should start from this document rather than recover the discarded implementation.

Retry results

The retry reconstructed the QBE-only chain, its positive proof, two fail-closed proof tests, and a boundary oracle. The first focused run self-hosted but passed 116/117: only the positive selector missed. In-process IL inspection showed that the raw array helper, fraw accumulator, non-promoted scratch slots, label references, and control shape were all present. The stale rejection was value_live_from(error_pos, product_slot): that edge branches before the product instruction, so the raw product has not been defined there. Its unique cold successor ends in disrupt and cannot reach the product join. Removing only that logically inapplicable pre-definition check left the structural cold edge proof intact. The final focused run then passed 117/117, including the positive selector and both rejection tests.

The required boundary oracle expected 63 from both Mach and native. Its null-element case deliberately enters the original numeric diagnostic edge and attempts to catch the disruption. Mach propagated that disruption out of the benchmark thunk instead. Rewriting the catch wrapper from a decorated function declaration to the repository’s established decorated function-expression form failed identically. Both attempts stopped in measure_run before a Mach result was recorded; native compilation did not begin. Under the acceptance gate this means the representation boundary remains unproven, so spectral IL/assembly comparison and alternating A/B timing were not run. The emitter changes, proof tests, benchmark registration, and oracle were deleted. No default-suite or fuzz claim is made for the deleted candidate.

Tooling findings

  • A cold rebuild can expire before the target publishes its descriptor. The descriptor’s 600-second command deadline is learned after realization, so it cannot protect that earlier phase. The CLI printed only reply timeout while compilation continued in the shop. A warm retry used the completed cache.
  • After one focused daemon run, the exact worktree process was alive and idle but its endpoint was unavailable. It was killed by exact PID; PIT_INPROCESS=1 then provided deterministic, serialized inspection and focused execution.
  • Temporary diagnostic string construction inside the already-large emitter made compiler range analysis pathological. The exact isolated process stayed at roughly 100% CPU for 10 minutes 46 seconds and was terminated at the agreed cap. That diagnostic expansion was removed completely. The restored candidate itself self-hosted in about one minute, so the blow-up is recorded as rejected diagnostic/tooling behavior, not target-code behavior.

The practical tooling fixes are to apply an explicit realization/build deadline independently of a command’s descriptor deadline, surface the active compile phase when an internal request expires, and provide an owned in-process/offline compiler-dump command so IR inspection does not require a daemon lifecycle.

Source: plans/archive/perf-2026-07/perf-spectral-inline-raw-chain.md