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

Native complete checked-array-store fold

Status: accepted isolated experiment on 530a9fcf; not merged upstream.

Hypothesis

M5 made the final array store itself fast, but fannkuch still executed the entire semantic checked-index diamond around every store:

is_int pred, key
jump_false pred, bad
length len, array
lt scratch, key, 0
jump_true scratch, bad
gt scratch, key, len
jump_true scratch, bad
store array, value, key
jump done
bad: disrupt
done:

The native emitter already has all information needed to implement this region directly. Doing so should remove tagged predicate construction, three scratch frame round trips, a generic length operation, and redundant branches while preserving the same observable bad-index and append behavior. This is a native-only lowering; semantic mcode and Mach output must remain unchanged.

Accepted implementation

qbe_emit scans final, fact-indexed mcode and selects only the exact ten-entry shape above. Selection additionally requires:

  • an independently exact array type at the store use;
  • an exact point-specific integer constant 0 for the lower-bound test;
  • the bad label immediately after the source jump and exactly the three shown incoming references;
  • a private done label with exactly the source jump as predecessor;
  • distinct predicate/length/comparison scratch slots that are neither captured nor live at the bad or done entries;
  • no disruption handler in the function.

Any label, instruction, slot alias, fact gap, handler, extra predecessor, or live scratch value makes the recognizer decline. The emitter leaves both source labels in place and skips only the proven private hot region.

The replacement first tests the integer tag and signed lower bound. It then uses the existing semantics-complete direct store lowering at the original store instruction index, so receiver/RHS type spans and write-barrier decisions remain exact. The direct path:

  • chases a forwarded array;
  • rejects key > length, while retaining key == length append semantics;
  • checks capacity and calls the ordinary rooted growth path when necessary;
  • preserves the stone check and disruption path;
  • stores the value and retains the old-to-young generational write barrier.

No semantic mcode operation, runtime object layout, Pit frame slot, actor state, heap allocation, per-program table, or serialized metadata was added. The only new state is temporary compiler data inside the QBE emitter. An initial scanner used many function locals and made self-hosted QBE emission inflate toward a 2 GiB compiler heap; that version was rejected before timing. Packing scanner temporaries into one nested record restored ordinary roughly 128 MiB compiler memory. Compiler speed and temporary memory are not campaign goals, but catastrophic self-host overhead is still not acceptable.

Generated-code evidence

Focused fannkuch.ce output on the exact parent and candidate:

artifactparentcandidatedelta
QBE IL178,984 B167,625 B-11,359 B (-6.35%)
assembly117,570 B114,287 B-3,283 B (-2.79%)
linked __text32,160 B31,036 B-1,124 B (-3.50%)
dylib file / mapped __TEXT87,648 B / 49,152 B87,648 B / 49,152 Bunchanged page footprint

The generated dylib data, bss, and other mapped segments are unchanged. This therefore improves target code density without spending target RAM. The mapped page count is unchanged for this one host fixture because both text sections fit the same page-aligned segment; a ROM image without Mach-O page padding retains the 1,124-byte text reduction.

Performance

Fresh-process, eleven-sample medians were interleaved to control thermal drift. The median of the three process medians is the claimed result:

armprocess medians (ms)median (ms)
exact parent 530a9fcf201.222, 184.077, 185.108185.108
complete-store fold117.495, 112.742, 113.575113.575

That is a 38.6% native fannkuch speedup with the independent result 3000008629. The first pair was thermally higher in both arms; the two later brackets independently show approximately the same 38-39% improvement.

A final dual-lane matrix measured Mach 226.3 ms and native 117.2 ms, with exact results. Mach is intentionally unchanged. array_grow produced a byte-for-byte identical focused dylib on parent and candidate and stayed around 1.9 ms; its source does not contain this checked diamond. Mandelbrot also produced the same dylib and stayed around 8.9 ms. Spectralnorm and binarytrees retained exact results; neither selected this fold in the focused artifacts. The generational array-barrier oracle, stone-write oracle, growth microbenchmark, and fannkuch all matched Mach in the final matrix.

Correctness gates

  • Compiler lowering tests: 95/95.
  • Added a positive exact-region fixture that requires the upper-bound branch, absence of the length helper, and presence of the pointer write barrier.
  • Added a negative fixture with a second incoming bad-label edge; it must retain the original checked diamond.
  • Default suite outside the restricted socket sandbox: 1,918/1,918.
  • Deterministic differential fuzz, 500 iterations at seed 20260713: 3,733/3,733.
  • Dual-lane barrier, stone, growth, and fannkuch matrix: all exact.

The first sandboxed default-suite attempt reported six local bind/port failures (Operation not permitted) and 1,901 passes. Running the identical suite with normal local-socket permissions immediately passed all 1,918 tests; this was a test-environment restriction, not a product failure.

Tradeoff and disposition

This is a favorable constrained-target trade: large AOT speedup, smaller generated text, no target data or runtime memory, no ABI or language change, and a conservative decline path. The cost is 142 source lines in the already large QBE emitter plus compiler-only scanning work. Keep it for composition and retest after the record-template, structured-TCO, and recursive-call branches are combined.

Source: plans/archive/perf-2026-07/perf-native-checked-store-region.md