Kernel Maps and Provider Selection

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

Two different "kernel map" systems exist in this repository.
The top-level kernel_maps/README.md documents the older 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.

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 284 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 — 284 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.

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. Only production providers auto-select; candidate, diagnostic, and deprecated never do.
priorityintegerRanks providers within one equivalence group only. It cannot compare across groups.
equivalence_groupnon-empty stringMandatory. A 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.

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.
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):

{
  "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.
{
  "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:

{
  "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 and _rank_provider_matches (build_ir_v8.py:5666-5770). The pipeline:

Resolver flow. 284 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 provider must declare an equivalence_group; a missing or malformed selection block is a HARD KERNEL SELECTION FAULT. Providers with no selection block at all are treated as legacy.
  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.

Evidence, not assertion. The X-Ray fixture version/v8/tests/fixtures/xray/provider_selection_trace.json records a real selection 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.

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.

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: 32 hardened maps, 32 interface and ABI cross-validated, 131 map-owned call ABI, 43 selection-managed, 25 legacy-interface-ready, with ceilings of 54 contract-pending, 191 legacy, 73 legacy selection conditionals and 35 operation-specific conditionals. 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: floors may only rise, ceilings may only fall. The audit currently reports the baseline values exactly — this is the starting line the burn-down is measured from:

MetricBoundDirection
hardened maps≥ 32floor — only rises
interface + ABI cross-validated≥ 32floor — only rises
map-owned call ABI≥ 131floor — only rises
selection-managed maps≥ 43floor — only rises
legacy-interface-ready maps≥ 25floor — only rises
contract-pending maps≤ 54ceiling — only falls
legacy maps≤ 191ceiling — only falls
legacy selection conditionals≤ 73ceiling — only falls
operation-specific conditionals≤ 35ceiling — only falls

Is This Over-Engineered?

Fair question — the project owner has asked it directly. 284 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

284 maps to maintain. 73 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, 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