Archive — history, not state. Kept for its reasoning and its evidence; its plan is closed.
Mach exact type-span consumption and constant-load fusion audit
Date: 2026-07-13
Base: f797cc91
Experiment branch: codex/perf-mach-exact-fusion
This experiment had two independent questions:
- Can Mach replace the
_has_inline_splicewhole-function fact ban with the exact final-index_type_spansalready produced for native lowering? - Does an adjacent numeric
LOADI/LOADKplus arithmetic/comparison superinstruction occur often enough to justify more VM text?
The first arm is accepted on this branch. The second was implemented, measured, rejected, and deleted. Neither arm adds a semantic mcode operation or changes runtime object/frame layout.
A. Exact facts on inline-spliced hosts: accepted
Old behavior
One inline splice set _has_inline_splice on the host. Mach lowering then
discarded the host’s entire _write_types and _no_overflow tables. That was
a necessary emergency guard after composed fannkuch exposed a false
add_iip claim, but it also disabled valid typed arithmetic everywhere else in
the host.
New selection rule
Mach now extracts only the int entries from _type_spans and asks for both
operand types at the current final semantic-mcode instruction index. The rule
is deliberately asymmetric:
- inline-spliced host: both operands must have exact covering
intspans; function-global_write_typesis never a fallback; - non-spliced host: exact spans are preferred, then the existing conservative function-global fact may be used;
_no_overflowis still consumed only at its exact final instruction index.
The extracted C table is compiler-transient. It has three int fields (12
bytes) per source type span during Mach lowering and is freed before lowering
returns. No new actor heap, frame slot, object header, or executable-code
sidecar is retained.
The existing serialized instr_facts byte table is now freed immediately
after load-time opcode selection. The selected opcode byte contains the entire
runtime decision; retaining one fact byte per instruction in shared code was
dead state. The original serialized bytes remain in the code-cache key, so
cache identity remains exact.
Historical failure, now made exact
Fannkuch’s former failure was at final mcode indices 576/577. The optimized
stream now records one operand as int and the overflow-promoted carrier as
num. Exact Mach lowering therefore leaves both operations generic. Under the
debug/ASAN build, with PIT_MACH_DEBUG_CONTRACTS active, full fannkuch returns
the exact 3000008629 result without a fact assertion.
aot_bench/regress/mach_inline_fact_spans.ce is a compact automated version:
it forces a known function to inline, obtains arguments through an array, and
checks the 30 * 100000000 + 8629 promotion chain in both Mach and native.
tests/compile.cm additionally verifies that the post-multiply add does not
have two exact int operand spans before executing its Mach blob.
Controlled performance
The experiment branch temporarily used
PIT_MACH_NO_EXACT_SPAN_FACTS=1 to disable only exact facts on spliced hosts.
That made the controlled pair leave the runtime binary, all other fusion,
source, compiler output, and native lane unchanged. The switch and disabled
path were deleted before integration; the accepted implementation always uses
the exact facts when they exist.
call_hot is a known-inlined numeric host and is the real coverage row:
| state | dynamic handlers | dispatches | serialized Mach | median |
|---|---|---|---|---|
| old splice ban | add 4,000,000; mul 2,000,000; fuse_lt_br 2,000,001 | 18,000,025 | 3,944 B | 14.410 ms |
| exact spans | add_ii 4,000,000; mul_ii 2,000,000; fuse_lt_br_ii 2,000,001 | 18,000,025 | 4,144 B | 13.313 ms |
That focused 11-sample pair is 7.6% faster. Three independent process medians (each itself seven samples) were 13.22 ms exact versus 13.94 ms disabled, a more conservative 5.2% win.
The +200-byte artifact delta is the optional facts section for this small
inlined unit (5.1%). It is shared per cached program, not per actor, and is not
retained a second time in PitCodeRegister. The compiler’s already-existing
23 _type_spans occupy 1,056 live host bytes for this unit; this arm does not
create or enlarge that producer table.
The important negative coverage is also exact:
| row | exact typed-handler change | dispatch change | Mach bytes | result |
|---|---|---|---|---|
| fannkuch | none | 0 | 30,760 B, unchanged | 3000008629 |
| spectralnorm | none | 0 | 88,760 B, unchanged | 1623647009 |
Those programs’ hot arithmetic is genuinely num, not int. Exact spans avoid
the old miscompile but do not manufacture optimization coverage.
Runtime code-size tradeoff
Release binaries were built from clean worktrees with the same toolchain:
| metric | f797cc91 | accepted arm | delta |
|---|---|---|---|
| dylib file | 1,454,232 B | 1,454,264 B | +32 B |
__text | 1,029,324 B | 1,030,440 B | +1,116 B |
__cstring | 56,461 B | 56,502 B | +41 B |
The target cost is about 1.2 KiB shared runtime text plus facts bytes only in artifacts whose spliced code actually receives typed handlers. A 5-8% win on inlined integer loops is worth that bounded cost even for small-console profiles; unlike a cache or shape table, it has no per-actor multiplier. A future ultra-small distribution can still compile the feature out as a profile choice if 1 KiB is more valuable than the speed.
B. Adjacent numeric load/use fusion: rejected and deleted
The prototype added only two target-private heads:
- fused
LOADIplus generic numeric use; - fused numeric
LOADKplus generic numeric use.
The second word stayed unchanged and independently targetable, and the handler always wrote the load’s carrier slot before performing the use. Eligible uses were add/subtract/multiply/divide and the six comparisons. The loader refused to replace a fact-typed integer operation or a comparison already eligible for compare/branch fusion, because either existing specialization removes at least as much work.
It passed the selected cross-lane matrix, but normal optimization removes the adjacency that the opcode needs: LICM hoists constants, while dynamic numeric guards sit between the remaining loads and their uses.
float_math: zero dynamic fused uses.arith_intandcall_hot: zero; their uses already had stronger typed handlers.- fannkuch: exactly one dispatch removed out of 232,394,869; paired seven-run medians were 290.63 ms disabled and 292.58 ms enabled (noise/regression).
- spectralnorm: exactly one dispatch removed out of 224,432,185.
- serialized Mach was byte-identical under the load-time toggle; no per-code state was added.
The prototype changed the dylib file by +176 bytes, while compiler/linker
layout and tail-merging made raw __text move by -5,504 bytes; neither size
signal matters beside the decisive absence of dynamic coverage. The two
opcodes, handler, toggle, and tests were deleted. A future constant-operand
experiment should consume non-adjacent _const_spans at the arithmetic use and
remove actual loads/carrier stores; adjacency fusion is the wrong lever for the
current optimized stream.
Tooling correction
aot_bench/census.ce now implements the same typed one-shot run command as
the focused benchmark tool. Previously the legacy bare-script help probe could
kill it before it printed a long composed census. The actor now replies with
the exact result, dispatch total, serialized Mach bytes, and median while
retaining its per-opcode console census. This change is committed separately
from the performance arm.
Validation receipts
- release fannkuch: exact
3000008629; - debug+ASAN fannkuch with Mach fact contracts: exact
3000008629, no contract violation; - selected native/Mach matrix: regression, call_hot, fannkuch, spectralnorm all match;
- targeted compiler suite after regression addition: 73/73;
- first cold default-suite attempt: 832/833, with only
vm_suitetiming out while its realization was still charged to the deadline; - isolated warm
vm_suite: 1,086/1,086; - warm default suite: 1,918/1,918;
- seeded fuzz,
./pit fuzz 500 --seed 20260713: 3,733/3,733.
The cold timeout is the existing daemon/realization deadline problem, not a wrong-result or crash: the same suite passed immediately after realization, and its isolated language/VM payload was fully green.
Source: plans/archive/perf-2026-07/perf-campaign-mach-exact-spans.md