Kernel Maps and Provider Selection

How CKE v8 decides which physical C kernel executes each logical operation — and why that decision lives in 346 machine-checked JSON maps instead of in Python conditionals.

Two different "kernel map" systems exist in this repository.
version/legacy/v6.6/legacy_kernel_maps/README.md documents the older v6/v7-era map format (decoder layer plans and buffer layouts consumed by gen_kernel_specs.py). This page is about the v8 provider-selection system in version/v8/kernel_maps/*.json — a different format with a different job. Do not confuse the two.

Choose Your Path

Understand the maps

Start with the annotated anatomy and the six field groups; background math lives in Deep Dive Concepts.

Add a provider

Follow the end-to-end contributor recipe: map JSON → schema validation → registry → parity → generated C.

Diagnose a selection

Read the resolver trace — every rejection carries a recorded reason; the full evidence pipeline is X-Ray.

Add an ISA variant

Variants are gated by requires in the map's impl block — see the field reference and v8 Kernel Architecture.

Define scratch / arenas

See memory planning: sizing formulas, alignment, arena offsets, and fail-closed validation; runtime-side setup is in the v8 Runbook.

The Three-Level Separation — and Why

One CKE runtime serves many model families × quantization formats × ISA variants × execution phases, all on CPUs. That product space is why v8 separates what math is required from which provider runs from how the op is physically executed:

Four-level stack. Level 1, the circuit in version/v8/circuits, owns what math is required: op instances, named port edges and required numerical contracts, and must not name physical provider IDs. Level 2, the DSL resolver in build_ir_v8.py, owns which provider runs: mechanical matching on contract, phase, dtype, shape, layout, ISA and alias safety, with no model-name checks. Level 3, the 346 kernel maps in version/v8/kernel_maps, own how the op is executed: numerical-contract identity, port dtype, layout, stride, storage, persistent-state and alias semantics, call ABI, lifecycle and priority. Level 4, the C kernel, owns nothing but the math.

Circuits declare the logical

version/v8/circuits/*.json (e.g. qwen3vl.json, qwen35.json) declare op instances, named port edges, and required_numerical_contracts. For hardened ops a circuit must not name a physical provider ID — it states requirements, not implementations.

Kernel maps own the physical

version/v8/kernel_maps/*.json — 346 maps — own the operation interface, numerical-contract identity, port dtype/layout/stride/storage, prefill/decode phase support, persistent-state and alias semantics, the C call ABI, lifecycle status, and priority.

The DSL resolves mechanically

version/v8/scripts/build_ir_v8.py filters by contract, then by phase/dtype/shape/layout/ISA/alias compatibility, ranks by lifecycle then priority, and fails closed on ambiguity. No model-name checks are allowed in the resolver.

How A New Model Family Earns Support

Fast family bring-up is evidence of reuse only when the change lands in the correct ownership layer. A model circuit may declare a new graph composition; a kernel map may describe a genuinely new provider contract; the C source may implement that contract. The shared resolver and code generator must not grow a model-name branch to connect them.

1. Circuit owns graph semantics

Attention placement, residual order, routed/shared experts, vision or audio bridges, and persistent state are explicit circuit edges. See the current family evidence in the model and kernel matrix.

2. Maps own provider arithmetic

Each selected provider declares dtype, layout, numerical contract, call ABI, scratch ownership, phase, ISA eligibility, and reference evidence. Priority may choose performance only inside one equivalence group.

3. X-Ray proves composition

Leaf parity is necessary but insufficient. X-Ray records selected providers and compares layer, state, bridge, and final-logit boundaries against an independent implementation.

4. Runbook controls promotion

A coherent real-weight run remains bring-up evidence until the modality-specific full-model certification gate passes. Long context, quality, ISA, and performance are additional production gates.

Architecture scaling rule: a new family should increase the reusable circuit/provider vocabulary. If it instead adds model-name checks to shared lowering, duplicates an existing arithmetic contract, or bypasses the map-owned ABI, source integrity and DSL policy should reject it before numerical or performance claims are considered.

Measuring Bring-Up Novelty

The scaling rule above is measurable. The advisory script version/v8/scripts/report_model_novelty_v8.py answers one question: when a new model family is brought up, how much code changes outside its circuit, tensor map, genuinely new kernels, and evidence fixtures?

In git-range mode (--base SHA --head SHA) it classifies every changed file into ownership buckets — circuit JSON, model/tensor maps, kernel maps, kernel C source, core compiler, converters, tests/evidence, docs, other — and reports per-bucket file counts and line deltas. The core-compiler bucket (DSL lowering, code generators, memory planner) is THE metric: its target trend is zero. A bring-up that lands entirely in its circuit, maps, kernels, and evidence is one where the architecture absorbed the family for free. In circuit mode (--circuit NAME) it reports the operations a circuit uses, how many are shared with other circuits versus unique to it, the providers bound, and their tracked status. Missing metadata is reported as an explicit null with a "not tracked yet" note; the report never fabricates numbers.

The report is advisory only: it is not a CI gate, does not enforce dsl_policy.json caps, and never fails the tree. When a snapshot is written to version/v8/.cache/reports/model_novelty_latest.json, the architecture-contract dashboard surfaces it as a purely informational section.

Anatomy of a Kernel Map

version/v8/kernel_maps/memcpy.json is the canonical annotated example — the production map behind the residual_save op. Every field group has one job:

Six field groups of the memcpy kernel map. Identity: id memcpy, op residual_save, operation interface residual_save.memcpy_copy.v1, variant fp32_copy — the stable names circuit contracts bind to. Selection metadata: status production, priority 100, equivalence group, phases prefill and decode — schema-required, drives ranking, priority ranks only within one equivalence group. Ports: input src fp32 N read, output dst fp32 N write, no weights or scratch, with layout, access, storage class and consumption per port; alias and overlap rules live in constraints notes. Quant: weight null, activation fp32, output fp32 — the dtypes compatibility filters match. Map-owned call ABI: dims N, param _memcpy_bytes of type size_t, call ABI version 1 with dst, src and size — the exact C call arguments owned by the map. Implementation and constraints: function memcpy, default variant, byte copy with no arithmetic and no overlapping writable views.

Selection metadata is schema-enforced

The selection block is required by version/v8/schemas/kernel_provider_selection.schema.json and carries exactly four fields:

FieldTypeRule
statusproduction | candidate | diagnostic | deprecatedLifecycle rank. Explicit candidate, diagnostic, and deprecated providers never auto-select. A map without a selection block is an implicit legacy provider: it stays eligible as a compatibility fallback and ranks below production — migration debt, not an unreachable provider.
priorityintegerRanks providers within one equivalence group only. It cannot compare across groups.
equivalence_groupnon-empty stringMandatory once a selection block exists. An explicit provider that omits it is a HARD KERNEL SELECTION FAULT.
phasesnon-empty unique list of init | prefill | decode | training | backwardDeclares which execution phases the provider supports.

The hard rules: an equal-priority tie between explicit production providers is a fault, not a coin flip. And the map — not the code generator — owns the call ABI: dims, typed params (here _memcpy_bytes of type size_t), and a versioned call_abi block naming the exact argument list (dst, src, size) with each argument's source port. The alias and arithmetic contract is part of the map too: constraints.notes states this is a byte copy, performs no arithmetic, and is undefined for overlapping writable views.

Parameter Taxonomy: Six Field Groups

Every field in a kernel map answers exactly one of six questions. Keeping the groups distinct is what lets the resolver treat them differently: compatibility fields filter, ranking fields order the survivors, and memory/scheduling fields never influence numerics at all.

kernel map one JSON document per provider version/v8/kernel_maps/ 1 · identity & contract id · op · variant operation_interface numerical_contract numerical_capabilities the names a circuit binds to 2 · compatibility quant {weight, act, out} port dtype · shape · layout phases · ISA requires alias & overlap rules filter stage: reject with reason 3 · scheduling parallelization strategies preferred per phase chunk & alignment mins phase_selection never changes numerics 4 · memory scratch size_bytes formula alignment (power of two) lifetime: model_runtime weight_preparation + budget feeds the arena planner 5 · ranking selection.status priority (within group) equivalence_group direction · act preference orders survivors, after filters 6 · evidence tests: parity oracles reference: scalar oracle tolerances · bit_exact production.reference_comparison promotion requires proof groups 1–2 decide eligibility · group 5 orders what survives · groups 3–4 schedule and place · group 6 justifies promotion
click / tap the diagram to expand

Reading a Kernel Map: Field Reference

The anatomy figure above shows the shape of one map. This section is the member-by-member reference: what each field means, which component consumes it, and what happens when it is missing or wrong.

The format is a superset — not every map carries every field. The migration is in flight, so three vintages coexist and the audit (version/v8/scripts/audit_kernel_map_interfaces_v8.py) sorts every map into explicit buckets: selection-managed (a valid selection block), hardened (operation_interface + numerical_capabilities, where each capability entry requires contract_id, status, phases, function, explicit_selector, implementation, arithmetic per numerical_kernel_capability.schema.json), interface+ABI cross-validated (hardened, plus a call_abi whose ports entries cover every declared input/weight/output port exactly), and legacy (none of the above). A missing optional block moves a map into an older bucket; it is not an error. The examples below are all production and selection-managed, and each lacks a different piece of the hardened format — that is deliberate, and the page says so per example.
Full field-by-field reference table (19 rows)
FieldWhat it meansConsumed byIf missing or wrong
idRegistry identity of the provider.Resolver, X-Ray (selections and rejections are recorded by id), audits.Every map has one in practice; X-Ray and audit output key on it.
opThe logical operation this map provides (e.g. gemm, residual_save, kv_cache_store).Resolver — matches the circuit's op name.Wrong op: the provider simply never matches that operation.
operation_interfaceHardened interface identity (e.g. residual_save.memcpy_copy.v1, kv_cache_store.bf16.head_major.v1) that circuit contracts bind to.Resolver, audit.Optional. Absent: map stays in the legacy/contract-pending bucket. Present: the audit hard-fails unsafe port aliasing on the map.
variantHuman/machine tag for the variant (e.g. fp32_copy, bf16_cache, q8_0_w_q8_0_a).Resolver — direction ranking reads variant/kernel id to demote backward providers during inference.Optional; a misleading name can misrank inference vs backward.
quant {weight, activation, output}Weight, activation, and output dtypes of the provider.Resolver — dtype compatibility filtering and activation-preference ranking.Wrong dtype: the provider is filtered out for mismatched tensors (recorded as a rejection reason, e.g. weight_dtype_mismatch).
selection {status, priority, equivalence_group, phases}Selection metadata; see the schema table above.Resolver ranking, audit.The block itself is optional — absent, the provider is treated as legacy and never outranks a production provider. Present but invalid (bad status, non-integer priority, empty group, bad phases): audit failure; a missing equivalence_group is a HARD KERNEL SELECTION FAULT.
inputs / weights / outputs / scratchPort lists. Each port has name, dtype, symbolic shape, layout, and a desc; depending on map vintage also access, storage_class, consumption (memcpy, kv_cache_store) or placement, optional (gemm). storage_class: state marks persistent state rather than an ephemeral output.Resolver (dtype/layout/alias compatibility), lowering and the memory planner, audit cross-validation.Member sets vary by vintage — that is expected. On interface-declaring maps, an unsafe alias_of or writable overlap is an audit hard fault.
dimsSymbolic dimension names the call ABI and lowering resolve to integers (e.g. _memcpy_bytes = embed_dim × seq_len × 4 computed at lowering).Lowering, codegen.Missing/undeclared dims cannot be referenced by the call ABI.
paramsExtra typed scalar parameters beyond ports and dims (e.g. _memcpy_bytes of type size_t).Lowering, codegen.Optional; empty for most GEMM-class maps whose arguments are all ports and dims.
call_abiThe exact C argument list: ordered params, each with name and a namespaced source (activation:, weight:, output:, dim:, runtime:, const:, …), plus optional cast and ports.Codegen (emits exactly this call), _validate_kernel_call_abi in build_ir_v8.py, audit.Optional — absent, codegen falls back to legacy argument conventions and the audit counts the map as legacy ABI. Present but malformed (unknown field, version ≠ 1, missing name/source, bad source namespace): HARD CALL ABI FAULT.
parallelization {supported, preferred, strategies}Declared threading policy: which partitionings exist, which is preferred per phase, and per-strategy constraints (e.g. split by tokens vs by output features, minimum chunk, alignment).Lowering / thread dispatch.Optional; governs scheduling only, never numerics (reduction_order_effect is declared separately).
constraints {notes, alignment}The semantics contract in prose and machine-checkable alignment (e.g. K must be a multiple of 32; "byte copy, no arithmetic, no overlapping writable views").Humans, audits, parity reviewers.Optional but load-bearing: this is where alias and arithmetic promises live.
numerical_contractContract ID string naming the exact arithmetic (e.g. q8_0_weight_q8_0_input_llama_fp32_output). The audit calls maps carrying it "legacy contract-shaped".Resolver/contract validation, parity gates.Optional extension; where present it must name a real registered contract.
impl {function, sources, variants[]}Which C function fulfills the contract, which sources compile it, and ISA-gated variants — each with name, requires (ISA features), compile_flags, and optionally its own priority and shape_constraints.Codegen (build), resolver/ISA filtering (requires gates eligibility).An ineligible variant is filtered out, never guessed at; a missing source file fails the build.
testsUnit, bench, and parity oracles with tolerances (e.g. a bit-exact llama.cpp comparison).Parity gates, CI.Optional; memcpy's is empty — a pure byte copy has no arithmetic to oracle.
referenceThe scalar contract oracle: reference function, adapter, validation status, and external oracles. production.reference_comparison can demand bit_exact agreement.Parity gates.Optional extension; where present, the optimized path must match the oracle's reduction order.
phase_selection, production, implementationMap-specific extension blocks: per-phase kernel families and scheduling policy, the production function with its comparison requirement, and execution metadata (ISA dispatch style, storage formats, threading runtime).Lowering, resolver, parity gates.Optional; richer maps carry them, minimal maps omit them.

Worked Example 1: memcpy — the Baseline Read

memcpy.json is the minimal production map: one op, two ports, one computed parameter, no ISA variants, no numerical contract — a byte copy has no arithmetic to contract. Verbatim excerpt (… marks elided lines):

Verbatim map excerpt: memcpy.json
{
  "id": "memcpy",
  "op": "residual_save",
  "operation_interface": "residual_save.memcpy_copy.v1",
  "selection": {"status": "production", "priority": 100, "equivalence_group": "residual_save.memcpy_copy.v1", "phases": ["prefill", "decode"]},
  "variant": "fp32_copy",
  "quant": {"weight": null, "activation": "fp32", "output": "fp32"},
  "inputs":  [{"name": "src", "dtype": "fp32", "shape": ["N"], "layout": "contiguous", "access": "read",  "storage_class": "activation", …}],
  "outputs": [{"name": "dst", "dtype": "fp32", "shape": ["N"], "layout": "contiguous", "access": "write", "storage_class": "activation", …}],
  "dims": ["N"],
  "params": [{"name": "_memcpy_bytes", "type": "size_t", "desc": "Number of bytes to copy"}],
  "constraints": {"notes": "Byte copy implementing logical residual_save; performs no arithmetic. src and dst must not overlap …"},
  "impl": {"function": "memcpy", "sources": [], "variants": [{"name": "default", "requires": [], "compile_flags": []}]},
  "call_abi": {"version": 1, "params": [
    {"name": "dst",  "source": "output:dst",        "cast": "void*", …},
    {"name": "src",  "source": "activation:src",    "cast": "const void*", …},
    {"name": "size", "source": "dim:_memcpy_bytes"} ]},
  "tests": {}
}

How to read it. The resolver reaches this map when a circuit needs residual_save in prefill or decode: the selection block makes it eligible in both phases, quant matches FP32 activations, and the single default variant passes ISA filtering vacuously (requires: []). Codegen then emits exactly memcpy(dst, src, size) — no argument reconstruction. What it deliberately lacks: numerical_contract, numerical_capabilities, non-empty tests. The audit therefore classes it as selection-managed and interface-ready, not fully "hardened" — a legitimate state for a copy with one possible behavior.

Worked Example 2: gemm_nt_q8_0_q8_0 — the Rich One

The Q8 prefill GEMM shows every group memcpy omits: a named numerical contract, ISA-gated variants, map-owned ABI for seven arguments, and validation oracles.

Six field groups of the Q8 GEMM kernel map. Blue groups also exist in memcpy.json: identity plus selection with status production, priority 200 and prefill-only phases; ports A and B in Q8_0, optional FP32 bias, FP32 output C, with quant q8_0, q8_0, fp32; and a map-owned seven-argument call ABI. Purple groups are absent from memcpy.json: the numerical contract whose equivalence group name q8_0_weight_q8_0_input_llama_fp32_output encodes the arithmetic so priority never picks across numerics; ISA variants avx2_m2n4, avx2 and avx_vnni gated by requires and compile flags; and validation oracles with a scalar reference function and a bit-exact llama.cpp parity test.
Verbatim map excerpt: gemm_nt_q8_0_q8_0.json
{
  "id": "gemm_nt_q8_0_q8_0",
  "op": "gemm",
  "selection": {"status": "production", "priority": 200,
                "equivalence_group": "q8_0_weight_q8_0_input_llama_fp32_output",
                "phases": ["prefill"]},
  "numerical_contract": "q8_0_weight_q8_0_input_llama_fp32_output",
  "quant": {"weight": "q8_0", "activation": "q8_0", "output": "fp32"},
  "inputs": [
    {"name": "A", "dtype": "q8_0", "shape": ["M", "K"], "layout": "token_major_contiguous", …},
    {"name": "B", "dtype": "q8_0", "shape": ["N", "K"], "layout": "opaque_packed", …},
    {"name": "bias", "dtype": "fp32", "shape": ["N"], …, "optional": true, …} ],
  "outputs": [{"name": "C", "dtype": "fp32", "shape": ["M", "N"], "layout": "token_major_contiguous", …}],
  "dims": ["M", "N", "K"],
  "constraints": {"alignment": {"K": 32}, "notes": "Q8_0 requires K dimension multiple of 32 …"},
  "phase_selection": {"decode": {"op_family": "gemv", "default_kernel": "gemv_q8_0_q8_0",
                                 "notes": "Single-token decode retains the certified GEMV path."}, …},
  "impl": {"function": "gemm_nt_q8_0_q8_0", "sources": ["src/kernels/gemm_batch_int8.c", …],
    "variants": [
      {"name": "avx2_m2n4", "function": "gemm_nt_q8_0_q8_0_m2n4", "priority": 240,
       "requires": ["avx2", "fma"], "shape_constraints": {"M_min": 2, "K_multiple": 32},
       "numerical_contract": "q8_0_weight_q8_0_input_llama_fp32_output"},
      {"name": "avx2",     "requires": ["avx2", "fma"],              "compile_flags": ["-mavx2", "-mfma"]},
      {"name": "avx_vnni", "requires": ["avx2", "avx_vnni", "fma"],  "compile_flags": ["-mavx2", "-mavxvnni", "-mfma"]} ]},
  "reference": {"function": "vec_dot_q8_0_q8_0_ref", "kind": "scalar_contract_oracle", …},
  "production": {"function": "gemm_nt_q8_0_q8_0", …, "reference_comparison": {"requirement": "bit_exact", …}},
  "call_abi": {"version": 1, "params": [
    {"name": "A", "source": "activation:a", "cast": "const void*"},
    {"name": "B", "source": "weight:_first_weight"},
    {"name": "bias", "source": "weight_f:_bias"},
    {"name": "C", "source": "output:c", "cast": "float*"},
    {"name": "M", "source": "dim:_m"}, {"name": "N", "source": "dim:_output_dim"},
    {"name": "K", "source": "dim:_input_dim"} ]},
  …
}

How the resolver uses it. Three facts interact:

Honesty note: this map has no operation_interface and no numerical_capabilities block, so the audit classes it as selection-managed and legacy-contract-shaped — a production provider carrying the older contract keys, exactly the kind of map the migration ratchet tracks.

Worked Example 3: kv_cache_store_bf16 — the Persistent-State One

Cache stores look like ordinary ops but their outputs are state, not ephemeral tensors. Verbatim excerpt:

Verbatim map excerpt: kv_cache_store_bf16.json
{
  "id": "kv_cache_store_bf16",
  "op": "kv_cache_store",
  "operation_interface": "kv_cache_store.bf16.head_major.v1",
  "selection": {"status": "production", "priority": 100,
                "equivalence_group": "kv_cache_store.bf16.head_major.v1", "phases": ["decode"]},
  "variant": "bf16_cache",
  "quant": {"activation": "fp32", "output": "bf16"},
  "inputs": [
    {"name": "k", "dtype": "fp32", "shape": ["KV", "D"], "layout": "head_major_contiguous",
     "access": "read", "storage_class": "activation", "consumption": "required", …},
    {"name": "v", …} ],
  "outputs": [
    {"name": "kv_cache_k", "dtype": "bf16", "shape": ["KV", "S_max", "D"], "layout": "head_major_contiguous",
     "access": "read_write", "storage_class": "state", "consumption": "required",
     "desc": "Packed BF16 K cache slice at current pos. Physical capacity S_max rows; valid-token count is tracked separately by the caller (append index); …"},
    {"name": "kv_cache_v", …} ],
  "dims": ["num_kv_heads", "head_dim", "num_layers", "max_seq_len", "KV", "S_max", "D"],
  "constraints": {"notes": "Scheduling may round the extent to physical capacity S_max; kernels must never read or write KV rows beyond the valid-token count declared by the append index."},
  "impl": {"function": "kv_cache_store_bf16", …, "sources": ["src/kernels/kv_cache_kernels.c"]},
  "call_abi": {"version": 1, "params": [
    {"name": "kv_cache_k", "source": "runtime:kv_cache_k_layer_u16", "cast": "uint16_t*", …},
    {"name": "kv_cache_v", "source": "runtime:kv_cache_v_layer_u16", "cast": "uint16_t*", …},
    {"name": "k", "source": "activation:k", "cast": "const float*", …},
    {"name": "v", "source": "activation:v", "cast": "const float*", …},
    {"name": "layer", "source": "const:0"}, {"name": "pos", "source": "runtime:pos"},
    {"name": "num_kv_heads", "source": "dim:num_kv_heads"},
    {"name": "head_dim", "source": "dim:head_dim"}, {"name": "max_seq_len", "source": "dim:max_seq_len"} ]},
  …
}

How to read it.

With the format and three real maps in hand, the resolution algorithm below is mechanical: everything the resolver knows about a provider comes from these fields, and everything it rejects leaves a recorded reason.

The Resolution Algorithm

Provider selection lives in _provider_selection_metadata, _rank_provider_matches, and find_kernel in version/v8/scripts/build_ir_v8.py. The pipeline:

Resolver flow. 346 candidate kernel maps enter compatibility filters — required numerical contracts, phase, dtype, shape, layout, ISA and alias safety — where any mismatch is rejected with a recorded reason and a missing equivalence group is a hard selection fault. Survivors rank by direction, activation preference, lifecycle with production first, then priority within one group only; candidate, diagnostic and deprecated never auto-select. Three outcomes: exactly one provider left is selected before GraphIR and preserved through generated C; no compatible provider fails closed as a compile-time error; an equal-priority production tie is a hard fault — ambiguous within a group, and priority cannot choose across groups. X-Ray records the selected provider plus every rejection reason, proving compatibility rejection precedes priority ranking.
  1. Validate selection metadata. Every explicit provider must declare an equivalence_group; a malformed selection block (bad status, non-integer priority, empty group) is a HARD KERNEL SELECTION FAULT. Providers with no selection block at all are implicit legacy: they stay eligible and rank below production. Explicit candidate, diagnostic, and deprecated providers are rejected here with a recorded reason.
  2. Filter by compatibility. Contract identity, phase, dtype, shape, layout, ISA, and alias safety — any mismatch rejects the provider with a recorded reason.
  3. Rank the survivors. The ranking tuple is direction (inference vs backward) → activation preference → lifecycle rank (production first) → priority (higher wins, within the group).
  4. Fail closed on ambiguity. A tie among explicit production providers raises "equal-priority production providers are ambiguous" when they share a group, or "priority cannot choose between different equivalence groups" when they do not. Zero compatible providers is also a compile-time failure — the build never silently substitutes different numerics.

A Real Resolver Trace

op residual_add · prefill · weight dtype fp32 — one X-Ray entry per decision provider status priority stage decision · reason candidate_900 candidate 900 provider_selection rejected — status_not_production:candidate wrong_phase_800 production 800 provider_selection rejected — phase_mismatch wrong_dtype_700 production 700 dtype_compatibility rejected — weight_dtype_mismatch compatible_100 production 100 priority_ranking selected — rank 0 version/v8/tests/fixtures/xray/provider_selection_trace.json — recorded by the live resolver, pinned verbatim by tests/test_v8_shared_provider_migration.py
click / tap the diagram to expand

Evidence, not assertion. This is not an illustrative mock-up: the X-Ray fixture version/v8/tests/fixtures/xray/provider_selection_trace.json records a real selection trace from the live resolver, and tests/test_v8_shared_provider_migration.py (test_filtering_precedes_priority_ranking) regenerates the trace and asserts it matches the fixture exactly — including that every rejection is recorded before any priority-ranking entry. In the trace, a candidate provider with priority 900 is rejected for status_not_production, a priority-800 production provider for phase_mismatch, and a priority-700 production provider for weight_dtype_mismatch — all before the compatible priority-100 production provider is selected at rank 0. Higher priority loses to incompatibility every time, because compatibility filtering happens before priority ranking. See X-Ray: Evidence and Divergence Attribution for the full evidence pipeline this trace belongs to.

Case Study: residual_save → memcpy

This is the headline example of DSL logic that legitimately remains — and of what hardened selection replaced. The two concerns are different, and the code now keeps them in different places:

Two lanes. DSL scheduling lane: the predicate should_insert_residual_save fires when the current op is a pre-norm, the next op starts a branch, and the previous op is not already residual_save; the op is auto-inserted before the pre-norm and stamped kernel memcpy; at lowering the byte size _memcpy_bytes equals embed_dim times seq_len times 4. Kernel-map lane: memcpy.json is selection-managed with operation interface residual_save.memcpy_copy.v1, production status, priority 100, prefill and decode phases, src read and dst write fp32 ports, constraints of byte copy with no arithmetic and no overlapping writable views, and a versioned call ABI of dst, src, size — copy semantics, not mislabeled residual arithmetic. Remaining debt box: template-override branches for rope_qk, rope_q, mrope_qk, position_embeddings, kv_cache_store_shared_q and assistant_layer_scale inside map_op_to_kernel, plus _make_decode_kv_store_op fabricating decode KV store ops; each migration moves selection into the maps and deletes the conditional.

Where the Weights Side Fits

Provider selection decides which kernel runs; the weights pipeline decides which bytes it reads. Converted BUMP weights carry a metadata sidecar describing tensor names, dtypes, and layouts (see GGUF to Bump and ADR 0006). Circuits and templates reference weight tensors, and the map's quant block and weights ports must match what the sidecar describes — a map that expects Q4_K weights will not pass compatibility filtering against an FP32 tensor. The conversion format itself is documented on the BUMP page; it is not re-explained here.

Memory Planning: From Port Shapes to a Checked Arena

Kernel maps declare memory symbolically; lowering turns the declarations into one arena with hard bounds. Nothing allocates outside this flow — a kernel that needs workspace names a scratch port with a sizing formula, and a kernel that keeps state marks the port storage_class: "state". The pipeline, all in build_ir_v8.py lowering with the arena emitted by codegen_core_v8.py:

1 · port shapes symbolic shapes from the map's ports: ["M","K"] · ["KV","S_max","D"] dims resolve to integers at lowering 2 · sizing formula scratch.size_bytes is symbolic: ceil(N/8)·(K/256)·sizeof(block) weight_preparation.prepared_bytes 3 · alignment every offset align_up to a power of two (≥ 64 B); non-power-of-two scratch alignment = HARD SCRATCH CONTRACT FAULT 4 · arena offset bump cursor assigns offsets; activations_base = align_up(weights_end, 64) layout carries one arena block 5 · live range lifetime: model_runtime scratch persists; storage_class: state (KV cache) is long-lived state, not a per-op allocation 6 · runtime validation _validate_lowered_activation_memory: offset + extent inside the arena, else hard fault — fail closed prepared weights weight_preparation repacks at load (e.g. q4_k_packed_vnni_x8) only when the aggregate fits the 2 GiB max_total_bytes budget; otherwise the named fallback provider serves persistent KV state physical capacity S_max rows allocated; valid-token count tracked separately via the append index (runtime:pos) — kernels never read or write beyond valid rows lowering in build_ir_v8.py · one bump arena emitted by codegen_core_v8.py · plans certified by certify_model_memory_plans_v8.py
click / tap the diagram to expand

The fail-closed property is the point: a map whose scratch formula overflows the arena, or whose alignment is not a power of two, fails the build with a named hard fault — the runtime never discovers the overflow by corruption. version/v8/scripts/certify_model_memory_plans_v8.py certifies per-model memory plans against a checked-in baseline without compiling or executing model code.

Contributor Recipe: Map JSON to Generated C

The full lane for adding or changing a provider, with the scripts that enforce each step:

  1. Author the map. Create or edit version/v8/kernel_maps/<id>.json: id, op, a selection block (status, priority, equivalence_group, phases), typed ports with dtype/shape/layout, and quant.
  2. Pass schema validation. tests/test_v8_provider_selection.py validates every map's selection block against version/v8/schemas/kernel_provider_selection.schema.json, and tests/test_v8_kernel_call_abi.py validates each declared call_abi against kernel_call_abi.schema.json; _validate_kernel_call_abi in build_ir_v8.py additionally hard-faults a malformed ABI at build time.
  3. Regenerate the registry. version/v8/scripts/ck_run_v8.py rebuilds version/v8/kernel_maps/KERNEL_REGISTRY.json — never hand-edit it.
  4. Run the audit. python3 version/v8/scripts/audit_kernel_map_interfaces_v8.py --check — the migration ratchet must not regress.
  5. Run the unit gates. tests/test_v8_provider_selection.py, tests/test_v8_kernel_call_abi.py, tests/test_v8_shared_provider_migration.py.
  6. Prove leaf parity. version/v8/scripts/parity_test_v8.py compares intermediate activations against llama.cpp dumps within declared tolerances.
  7. Inspect the resolver trace. Confirm your provider is selected (or rejected) for the reason you expect — every decision is recorded in the selection trace; the fixture above shows the format.
  8. Validate end to end. Codegen emits exactly the map-owned call ABI; version/v8/scripts/run_regression_v8.py (family regression lane) and stitched_parity_v8.py exercise the generated C. Promote candidate → production only with measured evidence.

Bringing up an entire model family on top of these maps — inventory, import adapter, circuit, generated-C inspection, oracle certification, standalone deployment — is walked end to end in the model bring-up guide.

Production-Readiness Checklist

Anti-Patterns the System Rejects

Anti-patternWhy it is dangerousWhat the map system does instead
hidden mallocA kernel allocating its own workspace bypasses the arena and defeats memory certification.All memory is declared — ports, scratch with size_bytes/lifetime — and codegen emits one bump arena with fail-closed bounds validation.
unknown ABI sourceA call argument drawn from an unsupported namespace means codegen would guess at a value.HARD CALL ABI FAULT: unknown field, version ≠ 1, missing name/source, or a source outside the declared namespaces fails validation.
model-name branches in the DSLif model == "…" in the resolver produces silently different numerics per family.Circuits state contracts; the resolver has no model-name checks. The remaining overrides are named debt tracked by the ratchet.
missing fallback providerAn ISA-gated or prepared path with no portable route leaves some hosts with zero providers.Zero compatible providers is a compile-time failure, never a silent substitution; prepared paths name an explicit fallback provider.
ISA leakageCompile flags or #ifdefs outside the variant system produce binaries that crash on unsupported hosts.Variants carry requires and compile_flags; ineligible variants are filtered out by the resolver, never guessed at.
equal-priority tieTwo production providers at the same priority is a coin flip between kernels.Hard fault: "equal-priority production providers are ambiguous" — resolve by changing priority or narrowing compatibility.
cross-group rankingComparing priority across equivalence groups swaps one arithmetic for another, silently.Hard fault: "priority cannot choose between different equivalence groups" — priority ranks within one group only.

Evolution Timeline

An honest, PR-dated history. The direction is monotonic: physics moves into the maps, heuristics leave the DSL.

Timeline with five milestones. Legacy era: kernel_bindings.json plus function-name ABI fallback, where naming conventions were the contract. PR 302: hardened provider selection with lifecycle status, priority and equivalence groups, and the operation-interface format. PR 305: shared RoPE, residual-copy and KV-cache providers migrated to maps. PRs 318 and 320: layout provider selection and direct-layout attention providers. Future, dashed: rebind circuits to hardened metadata and delete legacy branches, then attention and quantized GEMM migrations, ending in an agnostic DSL. A scoreboard strip shows the ratcheted burn-down of hardened versus legacy maps and resolver conditionals — the current audited values are in the Migration Scoreboard table below. The baseline is monotonic: the audit fails if a floor drops or a ceiling rises.

Migration Scoreboard

version/v8/contracts/kernel_interface_migration_baseline.json is a monotonic ratchet checked by version/v8/scripts/audit_kernel_map_interfaces_v8.py --check: floors may only rise, ceilings may only fall. The current audit values against the checked-in ratchet bounds:

MetricNow (audit)Ratchet boundDirection
hardened maps82≥ 46floor — only rises
interface + ABI cross-validated82≥ 46floor — only rises
map-owned call ABI219≥ 153floor — only rises
selection-managed maps86≥ 65floor — only rises
legacy-interface-ready maps55≥ 33floor — only rises
contract-pending maps49≤ 51ceiling — only falls
legacy maps not interface-ready154 (of 209 legacy)≤ 155ceiling — only falls
legacy selection conditionals59≤ 59ceiling — only falls
operation-specific conditionals28≤ 29ceiling — only falls

Generated from version/v8/kernel_maps/KERNEL_REGISTRY.json (346 maps) + version/v8/scripts/audit_kernel_map_interfaces_v8.py at commit 14edb65a5 (2026-09). Intended direction: these metrics should be regenerated during the docs build rather than hand-maintained — that pipeline is not implemented yet, so until it is, this page is updated manually against audit output.

Is This Over-Engineered?

Fair question — the project owner has asked it directly. 346 JSON maps, a schema for four fields, a ratchet file, and an audit script can look like architecture for its own sake. The honest answer has three parts.

Why the layering exists

One runtime serves many model families × quant formats × ISA variants × execution phases on CPUs. When provider choice is a Python conditional, the failure mode is not a crash — it is silently wrong numerics that pass leaf tests and corrupt a stitched model. Fail-closed selection converts that class of bug into a compile-time error with a recorded reason.

The costs are real

346 maps to maintain. 59 legacy selection conditionals still in the resolver. A migration still in flight, with named debt (RoPE, position embeddings, decode KV store) that has not yet moved into maps. Nobody should pretend this is finished or free.

Why it is not ceremony

The burn-down metrics are ratcheted and shrinking — the audit fails if they regress. The resolver has no model-name conditionals. Every layer is schema-validated and test-enforced: tests/test_v8_provider_selection.py, tests/test_v8_shared_provider_migration.py, tests/test_v8_kernel_call_abi.py, and the interface audit script.

The kill criterion is explicit: if the scoreboard metrics stopped moving — floors frozen, ceilings never shrinking — then the layering would be ceremony and should be torn out. As long as the ratchet keeps tightening and conditionals keep leaving the DSL, the structure is paying for itself in eliminated ambiguity.

Related: v8 Numerical Contracts, System Architecture, Composite Circuits (components + stitch), Code Generation, GGUF to Bump, and Architecture Links.

Image
100% | |
Scroll to zoom | Drag to pan | W/H to fit | 0 to reset | ESC to close