pub struct KernelBindingTable { /* private fields */ }Expand description
Clone is load-bearing, not a convenience: the process-wide table is
published as a copy-on-write snapshot (RwLock<Arc<KernelBindingTable>>
in dispatch.rs), so a writer clones the current table, mutates the
clone, and swaps the pointer. Readers therefore never hold a lock guard.
The clone is shallow-ish and cheap — BindingEntry is Copy and the
keys are Copy/SmallVec, so it is a HashMap bulk copy of POD, on a
write-rare path (backend init + runtime-fusion adoption).
Implementations§
Source§impl KernelBindingTable
impl KernelBindingTable
pub fn new() -> Self
Sourcepub fn register(
&mut self,
op: impl Into<BindingKey>,
dtypes: &[DType],
backend: BackendId,
kernel: KernelRef,
)
pub fn register( &mut self, op: impl Into<BindingKey>, dtypes: &[DType], backend: BackendId, kernel: KernelRef, )
Register a dispatch wrapper for (op, dtypes, backend) with the
default (all-false) capabilities. dtypes is the per-operand
dtype list — inputs in order, then outputs.
Phase 7.6 step 9a: multiple distinct kernels may register
against the same (op, dtypes, backend) key — they become
sibling alternatives at one decision point. Registration is
append-only and never panics; the same KernelRef
function pointer registered twice is detected by
Self::finalize (run once after bulk registration) and
returned as a Result, not raised as an inline panic.
PrecisionGuarantee defaults to
PrecisionGuarantee::UNAUDITED.
Step-7b convention: the always-built backend
(fuel-cpu-backend) runs Self::fill_unset_cpu_precision at
the end of its bulk registration pass to upgrade every UNAUDITED
CPU entry to PRIMITIVE_DETERMINISTIC_CPU (bit-stable per
hardware). Kernels with weaker guarantees should call
Self::register_with_precision explicitly before the fill
pass to opt out of the default.
Sourcepub fn register_with_caps(
&mut self,
op: impl Into<BindingKey>,
dtypes: &[DType],
backend: BackendId,
kernel: KernelRef,
caps: KernelCaps,
)
pub fn register_with_caps( &mut self, op: impl Into<BindingKey>, dtypes: &[DType], backend: BackendId, kernel: KernelRef, caps: KernelCaps, )
Register a dispatch wrapper with explicit capability flags. Used by binding sites that opt into kernel-side broadcast or other non-default behavior — the executor consults the caps to decide whether to auto-Contiguize inputs before kernel call.
Sourcepub fn register_with_precision(
&mut self,
op: impl Into<BindingKey>,
dtypes: &[DType],
backend: BackendId,
kernel: KernelRef,
precision: PrecisionGuarantee,
)
pub fn register_with_precision( &mut self, op: impl Into<BindingKey>, dtypes: &[DType], backend: BackendId, kernel: KernelRef, precision: PrecisionGuarantee, )
Register with an explicit crate::fused::PrecisionGuarantee.
Use this when a kernel has a precision claim that differs from
the bulk-fill default (e.g., a multi-threaded reduction with
non-deterministic accumulation order — bit_stable_on_same_hardware
must be false).
Sourcepub fn register_with_caps_and_precision(
&mut self,
op: impl Into<BindingKey>,
dtypes: &[DType],
backend: BackendId,
kernel: KernelRef,
caps: KernelCaps,
precision: PrecisionGuarantee,
)
pub fn register_with_caps_and_precision( &mut self, op: impl Into<BindingKey>, dtypes: &[DType], backend: BackendId, kernel: KernelRef, caps: KernelCaps, precision: PrecisionGuarantee, )
Backwards-compatible alias for full-form registration without
an explicit cost function. New code should prefer
Self::register_full.
Sourcepub fn register_full(
&mut self,
op: impl Into<BindingKey>,
dtypes: &[DType],
backend: BackendId,
kernel: KernelRef,
caps: KernelCaps,
precision: PrecisionGuarantee,
cost: CostFn,
)
pub fn register_full( &mut self, op: impl Into<BindingKey>, dtypes: &[DType], backend: BackendId, kernel: KernelRef, caps: KernelCaps, precision: PrecisionGuarantee, cost: CostFn, )
Phase 7.6 step 8: full-form register with caps + precision +
cost. Cost defaults to unknown_cost in the other
signatures; sites with non-default cost claims must use this
form (and not be overwritten by the bulk
Self::fill_unset_cpu_cost pass, which only touches
entries whose cost is still unknown_cost).
Phase 7.6 step 9a: appends to the alternative set for (op, dtypes, backend). Distinct KernelRef function pointers
compose as siblings. Append-only — a duplicate KernelRef is
detected by Self::finalize after bulk registration, not by
an inline panic (see Self::register).
Sourcepub fn register_full_with_source(
&mut self,
op: impl Into<BindingKey>,
dtypes: &[DType],
backend: BackendId,
kernel: KernelRef,
caps: KernelCaps,
precision: PrecisionGuarantee,
cost: CostFn,
kernel_source: &'static str,
)
pub fn register_full_with_source( &mut self, op: impl Into<BindingKey>, dtypes: &[DType], backend: BackendId, kernel: KernelRef, caps: KernelCaps, precision: PrecisionGuarantee, cost: CostFn, kernel_source: &'static str, )
Phase: backend-extensions refactor (2026-06-07). Same as
Self::register_full but tags the binding with a
kernel_source identifier used to distinguish kernels that
register at the same (op, dtypes, backend) key from
different implementation sources (AOCL vs MKL vs portable
CPU, etc.). The tag is diagnostic-only — the picker still
distinguishes alternatives by function-pointer identity.
Sourcepub fn register_full_with_source_generic(
&mut self,
op: impl Into<BindingKey>,
dtypes: &[DType],
backend: BackendId,
kernel: KernelRef,
caps: KernelCaps,
precision: PrecisionGuarantee,
cost: CostFn,
kernel_source: &'static str,
is_generic: bool,
kernel_revision_hash: u64,
cost_expr: Option<&'static CompiledCostExpr>,
)
pub fn register_full_with_source_generic( &mut self, op: impl Into<BindingKey>, dtypes: &[DType], backend: BackendId, kernel: KernelRef, caps: KernelCaps, precision: PrecisionGuarantee, cost: CostFn, kernel_source: &'static str, is_generic: bool, kernel_revision_hash: u64, cost_expr: Option<&'static CompiledCostExpr>, )
Full-form register with the FKC structural-fallback is_generic bit
(see BindingEntry::is_generic). The FKC importer computes the bit
once from a kernel’s retained ResolvedLayout set
(crate::fkc::is_generic_contract) and stamps it here;
every hand-written path routes through
Self::register_full_with_source, which passes false.
Otherwise identical to Self::register_full_with_source:
append-only, never-panic; the same KernelRef on one key is caught by
Self::finalize, not an inline panic.
Sourcepub fn finalize(&self) -> Result<()>
pub fn finalize(&self) -> Result<()>
Validate the fully-assembled table: within the alternative set
for any one (op, dtypes, backend) key, each KernelRef
function pointer must appear at most once. Distinct
alternatives compose as siblings; the same pointer twice is a
registration bug (a copy-paste in a hand-written table, or a
contract importer wiring one kernel onto two keys that collapse).
register* is append-only and never panics; this single pass —
run once after bulk registration — surfaces a duplicate as a
typed Result. The static init boundary
(crate::dispatch::global_bindings) .expect()s it (fail-fast
at startup for hand-written tables); the FKC contract importer
will call it with ?. This is the never-panic replacement for
the former inline duplicate panic!.
Sourcepub fn fill_unset_cpu_precision(&mut self, default: PrecisionGuarantee)
pub fn fill_unset_cpu_precision(&mut self, default: PrecisionGuarantee)
Phase 7.6 step 7b: upgrade every UNAUDITED-precision CPU
registration to the supplied default. Convention is to call
this at the end of bulk registration, so any
register_with_precision(...) calls that explicitly claimed a
non-default value are preserved. The architecture-target
shape would be precision-per-call-site, but for the ~335 CPU
primitive registrations that all share the deterministic
F32-accumulator property, a fill pass keeps the call sites
concise without sacrificing the architectural commitment
(every entry ends up with an explicit, non-UNAUDITED precision
claim before lookup is ever exercised).
Only entries with backend == BackendId::Cpu and the current
PrecisionGuarantee::UNAUDITED sentinel are touched. Non-CPU
backends register their own precision claims; non-UNAUDITED
entries are preserved.
Step 9a: applies to every alternative registered under a
CPU key, not just the first — so a key with N siblings all
starting at UNAUDITED ends with N entries upgraded to default.
Sourcepub fn fill_unset_cpu_cost(&mut self, dispatcher: fn(OpKind) -> CostFn)
pub fn fill_unset_cpu_cost(&mut self, dispatcher: fn(OpKind) -> CostFn)
Phase 7.6 step 8: upgrade every still-unknown_cost CPU
registration to the cost function returned by dispatcher for
its OpKind. The dispatcher’s contract: given an OpKind,
return the appropriate cost-family function — typically
crate::cost::default_cost_for_op_kind in production code,
but tests may pass their own dispatcher to exercise specific
cost shapes.
Convention is to call this at the end of bulk registration,
so any register_full(...) calls that explicitly claimed a
non-default cost are preserved. The function-pointer equality
check identifies UNKNOWN entries — any caller that wants a
“weaker” claim with non-zero values must use a distinct
function pointer (which won’t compare equal to unknown_cost
and thus won’t be overwritten).
Step 9a: applies to every alternative with a CPU backend —
each sibling entry at a key starting at unknown_cost is
upgraded independently.
Sourcepub fn fill_unset_cost_for_backend(
&mut self,
backend: BackendId,
dispatcher: fn(OpKind) -> CostFn,
)
pub fn fill_unset_cost_for_backend( &mut self, backend: BackendId, dispatcher: fn(OpKind) -> CostFn, )
Per-backend variant of Self::fill_unset_cpu_cost. Walks
every alternative whose backend matches backend and replaces
unknown_cost entries with the dispatcher’s choice for the
op. Used by [crate::vulkan_dispatch::register_vulkan_kernels]
to bulk-fill cost functions after per-kernel precision
registration; future MKL/AOCL/Metal backends register through
this same helper.
Backend-specific cost adjustments (e.g. higher
kernel_overhead_ns for Vulkan command-buffer submission vs
CPU’s nested-loop call overhead) are a Layer-2 refinement —
today’s default_cost_for_op_kind returns the same CPU-flavored
functions regardless of backend, which over-estimates Vulkan
throughput on small tensors. Either pass a Vulkan-flavored
dispatcher to this method, or rely on the empirical calibration
framework’s per-(op, dtype, backend) refinement.
Sourcepub fn lookup_precision(
&self,
op: impl Into<BindingKey>,
dtypes: &[DType],
backend: BackendId,
) -> PrecisionGuarantee
pub fn lookup_precision( &self, op: impl Into<BindingKey>, dtypes: &[DType], backend: BackendId, ) -> PrecisionGuarantee
Look up the crate::fused::PrecisionGuarantee for a
registered (op, dtypes, backend) triple. Returns the first
alternative’s precision; multi-impl callers wanting all
alternatives use Self::lookup_alternatives. Returns
PrecisionGuarantee::UNAUDITED if the binding is missing —
callers (notably the step-7b lint) treat that as “no claim”
rather than panicking.
Sourcepub fn lookup_cost(
&self,
op: impl Into<BindingKey>,
dtypes: &[DType],
backend: BackendId,
) -> CostFn
pub fn lookup_cost( &self, op: impl Into<BindingKey>, dtypes: &[DType], backend: BackendId, ) -> CostFn
Look up the CostFn for a registered (op, dtypes, backend)
triple. Returns the first alternative’s cost; multi-impl
callers wanting all alternatives use
Self::lookup_alternatives. Returns unknown_cost for
missing bindings — the step-8 lint treats that as “no cost
claim.”
Sourcepub fn iter_precision(
&self,
) -> impl Iterator<Item = (OpKind, &[DType], BackendId, PrecisionGuarantee)>
pub fn iter_precision( &self, ) -> impl Iterator<Item = (OpKind, &[DType], BackendId, PrecisionGuarantee)>
Iterate (op, dtypes, backend, precision) over every
registered alternative — one tuple per BindingEntry, not per
key. Used by the step-7b coverage lint, which groups by
OpKind and checks the bit-stable commitment per group; the
grouping handles N-alternatives-per-key naturally.
Sourcepub fn iter_entries(
&self,
) -> impl Iterator<Item = (OpKind, &[DType], BackendId, &BindingEntry)>
pub fn iter_entries( &self, ) -> impl Iterator<Item = (OpKind, &[DType], BackendId, &BindingEntry)>
Iterate (op, dtypes, backend, &entry) over every registered
alternative — one tuple per BindingEntry, mirroring
Self::iter_precision but yielding the FULL entry (by reference)
instead of projecting out just the precision field. Used by the
FKC empirical verification harness (Task 4.4+), which needs the
whole BindingEntry (the kernel fn pointer to invoke, caps,
kernel_revision_hash for ledger keys, …), not just one field.
Runtime-fused/JIT entries are filtered out (static_op() returns
None for them) — same “static-kernel audit view” as
iter_precision/iter_cost.
Sourcepub fn iter_cost(
&self,
) -> impl Iterator<Item = (OpKind, &[DType], BackendId, CostFn)>
pub fn iter_cost( &self, ) -> impl Iterator<Item = (OpKind, &[DType], BackendId, CostFn)>
Iterate (op, dtypes, backend, cost_fn) over every registered
alternative — one tuple per BindingEntry. Used by the step-8
coverage lint to check the “every primitive op has a
non-default cost function” commitment per OpKind.
Sourcepub fn iter_keys(&self) -> impl Iterator<Item = (OpKind, &[DType], BackendId)>
pub fn iter_keys(&self) -> impl Iterator<Item = (OpKind, &[DType], BackendId)>
Iterate every registered (op, dtypes, backend) decision-point
key — one item per key, regardless of how many alternatives
share it. Consumed by SystemTopology::build_at to derive
which backends have kernels registered + which (op, dtype)
pairs each backend covers.
Sourcepub fn has_runtime_fused(&self, fid: FusedOpId, backend: BackendId) -> bool
pub fn has_runtime_fused(&self, fid: FusedOpId, backend: BackendId) -> bool
Whether ANY runtime-fused entry is bound for (fid, backend),
regardless of dtype tuple — the coarse capability-gate query (the
optimizer’s fuse/don’t-fuse predicate is id-level; the dtype-precise
check is the dispatch-time Self::lookup_with_caps itself).
Sourcepub fn unique_runtime_fused(
&self,
fid: FusedOpId,
backend: BackendId,
) -> RuntimeFusedLookup<'_>
pub fn unique_runtime_fused( &self, fid: FusedOpId, backend: BackendId, ) -> RuntimeFusedLookup<'_>
Resolve (fid, backend) to a runtime-fused binding, refusing to pick
when the answer is ambiguous.
A FusedOpId names a RECIPE, not a signature: register_runtime_fused
deduplicates on the region’s base-map hash, so one id can legitimately
carry several dtype tuples — [F32, F32] and [F32, F32, F32] are
different arities under the same id. The pre-GAP-213 first_runtime_fused scanned
for (fid, backend) and binds dtypes without testing it, so it
answers such a question by HashMap::iter() order: a coin flip per
process, and capable of returning a kernel of the wrong operand arity.
The three outcomes are kept as three values rather than folded into an
Option, because not bound and ambiguous are different facts with
different fixes, and an Option would make them indistinguishable to the
caller — the same collapse that let GAP-001 read as a version regression
for eleven days.
Scope: this is the diagnostic/test resolution path. Production
dispatch never came through here — it uses Self::lookup_with_caps,
which takes the dtype tuple and does an exact keyed get, so it can
neither be ambiguous nor return a wrong arity. See GAP-213.
Sourcepub fn lookup(
&self,
op: impl Into<BindingKey>,
dtypes: &[DType],
backend: BackendId,
) -> Result<KernelRef>
pub fn lookup( &self, op: impl Into<BindingKey>, dtypes: &[DType], backend: BackendId, ) -> Result<KernelRef>
Look up the wrapper for (op, dtypes, backend). Returns the
first alternative’s KernelRef; for capability-aware
lookup use Self::lookup_with_caps; for the full alternative
set use Self::lookup_alternatives.
Sourcepub fn lookup_with_caps(
&self,
op: impl Into<BindingKey>,
dtypes: &[DType],
backend: BackendId,
) -> Result<(KernelRef, KernelCaps)>
pub fn lookup_with_caps( &self, op: impl Into<BindingKey>, dtypes: &[DType], backend: BackendId, ) -> Result<(KernelRef, KernelCaps)>
Capability-aware lookup. Returns the first dense-safe alternative
— the first sibling whose caps do NOT set
KernelCaps::requires_broadcast — paired with its registered
KernelCaps. A baked-broadcast sibling (broadcast_stride0: required) is SKIPPED, so a dense operand is never routed into a
kernel that reads in[0]; the exclusion is order-independent (path
1a). For every existing binding — none set requires_broadcast —
this is byte-identical to returning the first-registered alternative.
Surfaces Error::NoBackendForOp on a missing binding OR when every
sibling is broadcast-baked (production-correct: a surfaced miss, never
a wrong read, no panic).
Sourcepub fn coverage_diagnostic(&self) -> (Vec<BackendId>, Vec<CoverageBinding>)
pub fn coverage_diagnostic(&self) -> (Vec<BackendId>, Vec<CoverageBinding>)
Build the truthful (available_backends, supported_combinations)
diagnostic pair for a Error::NoBackendForOp miss: the DISTINCT
registered backends, and the full static (backend, op, dtypes) coverage
(the useful “what exists” context). Shared by
lookup_with_caps AND the plan-time miss
(plan::missing_binding_error) so BOTH surface the same real context. The
plan-time path formerly hardcoded [] here, which falsely reads as “no
backend is registered” and misdirects diagnosis — a mixed-precision
(op, dtypes) gap is not an empty registry (observed in the wild:
[F32, BF16, F32] matmul reported as “available backends: []”).
Sourcepub fn backend_supports(
&self,
op: impl Into<BindingKey>,
dtypes: &[DType],
backend: BackendId,
) -> bool
pub fn backend_supports( &self, op: impl Into<BindingKey>, dtypes: &[DType], backend: BackendId, ) -> bool
Does this specific backend serve (op, dtypes) with a usable
(non-broadcast-baked) binding?
Mirrors lookup_with_caps’s success condition
exactly — same key, same !requires_broadcast admission — but skips
constructing the miss diagnostic, so it is cheap enough to call in a
per-node loop (coverage_diagnostic walks the entire table).
Ask this once per candidate backend. There is deliberately no
“does any backend anywhere serve it?” sibling: a predicate that ignores
the key’s backend component cannot answer a dispatch question, because a
node executes on one backend rather than on the union of them. Such a
sibling did exist (any_backend_supports) and it caused a real defect —
the dtype-reconciliation pass consulted it, so a backend a node could
never run on suppressed the Op::Cast that node needed and planning
dead-ended at NoBackendForOp. See crate::plan::candidate_backends_for.
Sourcepub fn lookup_alternatives(
&self,
op: impl Into<BindingKey>,
dtypes: &[DType],
backend: BackendId,
) -> &[BindingEntry]
pub fn lookup_alternatives( &self, op: impl Into<BindingKey>, dtypes: &[DType], backend: BackendId, ) -> &[BindingEntry]
Phase 7.6 step 9a: return the full set of registered
alternatives at the (op, dtypes, backend) decision point.
Order is registration order — append-on-register is the step-9a
contract. The empty slice means “no kernel registered at this
decision point.”
The route picker (step 9b) consumes this to rank alternatives
by cost + precision at plan time. Single-impl callers continue
to use Self::lookup_with_caps (which returns the first
alternative — the route picker’s job is the longer-term home
for selection logic).
Trait Implementations§
Source§impl Clone for KernelBindingTable
impl Clone for KernelBindingTable
Source§impl Debug for KernelBindingTable
impl Debug for KernelBindingTable
Auto Trait Implementations§
impl Freeze for KernelBindingTable
impl RefUnwindSafe for KernelBindingTable
impl Send for KernelBindingTable
impl Sync for KernelBindingTable
impl Unpin for KernelBindingTable
impl UnsafeUnpin for KernelBindingTable
impl UnwindSafe for KernelBindingTable
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more