0%

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

  • CodegenSession contains immutable target/layout/ABI/runtime symbol tables and stable function ordering. Each worker owns all builders, scratch arenas, pass state, encoders, and diagnostics for one CodegenUnit; no worker mutates a module-wide IR, pass manager, context, allocator, symbol table, or diagnostics sink.
  • FunctionCodegenKey fingerprints 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.
  • CompiledFunctionFragment is 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) -> CompiledFunctionFragment boundary. 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-keyed ArcIrCache/ArtifactCache shape. Salsa remains upstream and ori_backend remains 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.