pub fn derive_backend_caps(
backend: BackendId,
device_location: DeviceLocation,
table: &KernelBindingTable,
) -> BackendCapabilitiesExpand description
FKC cost unification — Part A.
Derive a BackendCapabilities for backend (at device_location)
from the kernel binding table. This is the general (any-backend)
analogue of default_cpu_caps: instead of a hand-maintained list,
it reads the backend’s REGISTERED KERNELS out of the binding table
and advertises exactly the (OpKind, DType) pairs those kernels
cover.
The (op, dtype) derivation mirrors
crate::topology::SystemTopology’s binding_op_coverage: each
binding key carries per-operand dtypes (inputs then outputs); the
classic (op, dtype) shape takes the OUTPUT dtype — the last entry
in the key’s dtype list. Single-dtype keys (most elementwise ops)
are unambiguous; multi-dtype keys (e.g. QMatMul [F32, U32, F32])
still produce the consumer-relevant output-dtype entry.
Why this fixes the placement cost model. The placement cost
composer (ranker::cost::compute_static_costs) skips any candidate
whose backend has no BackendCapabilities in the registry —
leaving its static_cost at the default ZERO, so an uncapped GPU
candidate prices as free and can spuriously out-rank a real CPU
cost. Registering the caps this function derives makes
capabilities_for(gpu) return Some, so the GPU candidate’s
FLOP/byte cost fn actually runs (see also
KernelBindingTable::fill_unset_cost_for_backend, which ensures
those kernels carry a non-unknown_cost cost fn).
Alignment / granularity use conservative device defaults (256-byte
alignment for GPU buffers — covers CUDA’s 256B and Vulkan’s typical
storage-buffer alignment; byte-addressable). Transfer paths
advertise the universal HostStaging link back to the CPU so the
TransferMatrix can price GPU↔CPU crossings; richer paths (P2P,
shared memory) are a future refinement the backend can declare
explicitly. Never panics.