Skip to main content

KernelBindingTable

Struct KernelBindingTable 

Source
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

Source

pub fn new() -> Self

Source

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.

Source

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.

Source

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

Source

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.

Source

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

Source

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.

Source

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.

Source

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!.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.”

Source

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.

Source

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.

Source

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.

Source

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.

Source

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

Source

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.

Source

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.

Source

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

Source

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: []”).

Source

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.

Source

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

Source

pub fn len(&self) -> usize

Total number of registered alternatives across all keys (not just unique keys). Step 9a: a key with N siblings counts N.

Source

pub fn is_empty(&self) -> bool

Empty binding table.

Source

pub fn key_count(&self) -> usize

Total number of unique (op, dtypes, backend) keys. Step 9a: distinct from Self::len, which counts alternatives.

Trait Implementations§

Source§

impl Clone for KernelBindingTable

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for KernelBindingTable

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for KernelBindingTable

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more