s17B — Function-Granular Incrementality + Parallel Compilation
Goal
Native codegen becomes independently cacheable and schedulable per function before the optimizing mid-end arrives. Unchanged functions execute zero backend phases, invalidation follows explicit semantic dependencies instead of module boundaries, jobs=N compiles cache misses concurrently without a global lock, and stable finalization makes artifacts and diagnostics byte-for-byte deterministic across cache state and worker count.
Architecture Contract
CodegenSessioncontains immutable target/layout/ABI/runtime symbol tables and stable function ordering. Each worker owns all builders, scratch arenas, pass state, encoders, and diagnostics for oneCodegenUnit; no worker mutates a module-wide IR, pass manager, context, allocator, symbol table, or diagnostics sink.FunctionCodegenKeyfingerprints the realized function semantics; target triple/features; only referenced compiled layout/ABI entries; runtime-ABI and fragment-format versions; typed-fact fingerprint (reserved/empty for fast tier, populated at s18); tier and optimizer-pipeline version/config; and exact cross-function inputs consumed by a transform. A normal call edge alone does not hash the callee body. Inlining or another body-reading transform records the callee/body/fact dependency that it consumed.CompiledFunctionFragmentis the cache value: target code or WASM body, local constants, relocations, symbol contributions, plus unwind/debug contributions needed by deterministic finalization. The cache is content-addressed and checksum/version verified. Corruption, unknown schema, or target/config mismatch is a safe miss and recomputation, never trusted bytes or a silent fallback to stale data.- Finalization sorts fragments, symbols, constants, relocations, diagnostics, and debug/unwind contributions by stable IDs, never task completion order. It produces the same native object or complete WASM module for jobs=1, jobs=N, all-miss, and all-hit builds. System-link time is retained as its own measured phase.
- Exact invalidation rules are executable: a body-only edit with no consumed cross-function dependency invalidates one function; an ABI/layout/runtime/fact change invalidates the functions referencing that contract; a cross-function transform invalidates the recorded consumer closure. Broad “module changed” invalidation is a failing fallback, not an acceptable conservative implementation.
Implementation Sketch
- Refactor the already-conformant fast pipeline behind a pure
compile_function(CodegenUnit, &CodegenSession) -> CompiledFunctionFragmentboundary. Add phase-invocation counters under the s17 harness so “cache hit” proves no hidden translate/isel/regalloc/encode work. - Add a backend-neutral artifact-cache seam outside
ori_llvm; do not extend the current LLVM-private, module-keyedArcIrCache/ArtifactCacheshape. Salsa remains upstream andori_backendremains a pure consumer; backend fragment keys and storage are native-backend concerns behind the shared driver boundary. - Schedule cache misses through a bounded worker pool honoring the compiler jobs setting. Aggregate structured results after workers finish, sort deterministically, and finalize once. Record wait/work/finalize fractions so a lock or serial tail is visible.
- Use the s17 driver for cold/all-miss, warm/all-hit, one-body-edit, ABI/layout change, typed-fact change after s18, and jobs=1/jobs=N cases. On the generated large independent-function corpus, “scales” means the one-sided confidence bound for parallel/serial wall time is strictly below 1.0; noise-overlap does not pass.
- If system-link or whole-object finalization dominates the one-edit end-to-end gate, execute the s03 escalation decision (stable object partitions or the s22 in-memory path). Codegen-cache hit rate alone cannot close this section while user-observed latency remains unexplained.
Test Strategy
- Exact-count pins assert unchanged functions record zero translate/optimize/isel/regalloc/encode invocations; a one-body-edit case compiles exactly the changed function; ABI/layout/fact and inlining fixtures compile exactly the declared dependent closure.
- Determinism matrix: jobs {1,N} x cache {all miss, all hit, mixed hit} x target {x86-64,aarch64,riscv64,s390x,wasm}; compare artifact bytes, symbol/relocation tables, diagnostics, and observable execution.
- Negative pins: omit one key component and prove the stale-cache fixture is detected; corrupt a fragment and prove safe recomputation; randomize worker completion order and prove stable bytes; inject a backend-global lock and prove the scaling-sensitivity probe becomes a blocker.
Work Items
- Freeze the FunctionCodegenKey and precise invalidation contract over realized function semantics, target/features, compiled layout and ABI, typed-fact and runtime-ABI versions, optimization config, and declared cross-function dependencies.
- Refactor native codegen into independent per-function CodegenUnits with immutable shared session tables and worker-local scratch; BIR through encoded fragment must be reentrant and free of module-wide mutable state.
- Implement a content-addressed CompiledFunctionFragment cache plus deterministic module finalization; unchanged functions perform zero translate/optimize/isel/regalloc/encode work and corrupt or version-stale entries miss safely.
- Implement deterministic jobs=1 and jobs=N compilation with stable diagnostics and byte-identical artifacts; prove multicore wall-clock improvement on the generated large-function corpus without a global backend lock.
- Add cold, warm, one-body-edit, ABI-change, and fact-change benchmark cases with per-phase invocation counts, finalization/link time, and peak RSS; pin exact invalidation closure, cache sensitivity, and serial/parallel equivalence.