fuel_dispatch/fkc/cpu_link.rs
1// SPDX-License-Identifier: MIT OR Apache-2.0
2//! The built-in CPU backend's FKC `link_registry` (kernel-seam-interop §3.5,
3//! §4.3; FKC §12.6). Maps each CPU kernel contract's `entry_point` symbol to
4//! the production dispatch wrapper — the real, non-stub resolution the importer
5//! uses so an imported contract binds the **actual** kernel (no raw pointers in
6//! the serialized contract, FKC P9).
7//!
8//! For the built-in CPU backend the wrappers and this table co-locate in
9//! fuel-dispatch — the dispatch layer that adapts raw byte-kernels to
10//! [`KernelRef`]. An *external* provider (e.g. Baracuda) instead exports its own
11//! link registry across the FFI; this is Fuel's internal-provider analogue, and
12//! the first FKC conformance reference.
13
14use crate::fkc::lower::LinkRegistry;
15use crate::kernel::KernelRef;
16
17/// One `(contract entry_point symbol, production wrapper)` pair. The symbol
18/// matches the contract's `entry_point: "fuel_cpu_backend::byte_kernels::<op>_<dt>"`.
19macro_rules! ep {
20 ($op:literal, $dt:literal, $wrapper:ident) => {
21 (
22 concat!("fuel_cpu_backend::byte_kernels::", $op, "_", $dt),
23 crate::dispatch::$wrapper as KernelRef,
24 )
25 };
26}
27
28/// Like [`ep!`] but for a wrapper whose contract `entry_point` symbol is its
29/// FULLY-QUALIFIED `fuel_dispatch::dispatch::<wrapper>` path. The FlashAttn
30/// BACKWARD wrappers live in the dispatch layer (they select which gradient the
31/// shared byte-kernel writes via a `FaBackwardWhich` selector), so their
32/// contract `entry_point` carries the `fuel_dispatch::dispatch::` prefix — NOT
33/// `ep!`'s `fuel_cpu_backend::byte_kernels::<op>_<dt>` byte-kernel shape.
34macro_rules! ep_dispatch {
35 ($wrapper:ident) => {
36 (
37 concat!("fuel_dispatch::dispatch::", stringify!($wrapper)),
38 crate::dispatch::$wrapper as KernelRef,
39 )
40 };
41}
42
43/// One `(FUSED-contract entry_point symbol, production wrapper)` pair for a
44/// `fused_op` section. Unlike the primitive [`ep!`] / [`ep_dispatch!`] pairs, a
45/// fused contract declares a **dtype-agnostic BASE** `entry_point`
46/// `fuel_dispatch::dispatch::<op>_cpu` (e.g. `…::softmax_last_dim_cpu`) plus a
47/// per-input `dtypes: [F32, F64, BF16, F16]` list, so [`crate::fkc::lower::lower_fused`]
48/// **dtype-fans** it (§3.4, the fused analogue of the primitive fan-out): per
49/// fanned `dt` it resolves `<base>_<dt>` (e.g. `…::softmax_last_dim_cpu_f32`)
50/// through this table. Each such fanned symbol binds the `<op>_<dt>_cpu_wrapper`
51/// — the exact kernel fn the hand-written `register_default_fused_kernels` seam
52/// registers for that op's `dt` dtype tuple. Note the naming skew: the FANNED
53/// symbol is `<op>_cpu_<dt>` (base `…_cpu` + `_<dt>` suffix), while the wrapper
54/// fn is `<op>_<dt>_cpu_wrapper` (`_cpu` after `_<dt>`), so the mapping is
55/// spelled out per (op, dtype) row.
56macro_rules! fep {
57 ($symbol:literal, $wrapper:ident) => {
58 (
59 concat!("fuel_dispatch::dispatch::", $symbol),
60 crate::dispatch::$wrapper as KernelRef,
61 )
62 };
63}
64
65/// The CPU elementwise-binary family's `symbol → production wrapper` map
66/// (8 ops × 4 dtypes). The chassis umbrella section is `registrable: false`
67/// (§3.10 describe-only), so it never reaches resolution and is absent here.
68pub static CPU_BINARY_ENTRY_POINTS: &[(&str, KernelRef)] = &[
69 ep!("add", "f32", add_elementwise_f32_cpu_wrapper),
70 ep!("add", "f64", add_elementwise_f64_cpu_wrapper),
71 ep!("add", "f16", add_elementwise_f16_cpu_wrapper),
72 ep!("add", "bf16", add_elementwise_bf16_cpu_wrapper),
73 ep!("sub", "f32", sub_elementwise_f32_cpu_wrapper),
74 ep!("sub", "f64", sub_elementwise_f64_cpu_wrapper),
75 ep!("sub", "f16", sub_elementwise_f16_cpu_wrapper),
76 ep!("sub", "bf16", sub_elementwise_bf16_cpu_wrapper),
77 ep!("mul", "f32", mul_elementwise_f32_cpu_wrapper),
78 ep!("mul", "f64", mul_elementwise_f64_cpu_wrapper),
79 ep!("mul", "f16", mul_elementwise_f16_cpu_wrapper),
80 ep!("mul", "bf16", mul_elementwise_bf16_cpu_wrapper),
81 ep!("div", "f32", div_elementwise_f32_cpu_wrapper),
82 ep!("div", "f64", div_elementwise_f64_cpu_wrapper),
83 ep!("div", "f16", div_elementwise_f16_cpu_wrapper),
84 ep!("div", "bf16", div_elementwise_bf16_cpu_wrapper),
85 ep!("maximum", "f32", maximum_elementwise_f32_cpu_wrapper),
86 ep!("maximum", "f64", maximum_elementwise_f64_cpu_wrapper),
87 ep!("maximum", "f16", maximum_elementwise_f16_cpu_wrapper),
88 ep!("maximum", "bf16", maximum_elementwise_bf16_cpu_wrapper),
89 ep!("minimum", "f32", minimum_elementwise_f32_cpu_wrapper),
90 ep!("minimum", "f64", minimum_elementwise_f64_cpu_wrapper),
91 ep!("minimum", "f16", minimum_elementwise_f16_cpu_wrapper),
92 ep!("minimum", "bf16", minimum_elementwise_bf16_cpu_wrapper),
93 ep!("pow", "f32", pow_elementwise_f32_cpu_wrapper),
94 ep!("pow", "f64", pow_elementwise_f64_cpu_wrapper),
95 ep!("pow", "f16", pow_elementwise_f16_cpu_wrapper),
96 ep!("pow", "bf16", pow_elementwise_bf16_cpu_wrapper),
97 ep!("rem", "f32", rem_elementwise_f32_cpu_wrapper),
98 ep!("rem", "f64", rem_elementwise_f64_cpu_wrapper),
99 ep!("rem", "f16", rem_elementwise_f16_cpu_wrapper),
100 ep!("rem", "bf16", rem_elementwise_bf16_cpu_wrapper),
101];
102
103/// The CPU out-of-place scalar-param family's `symbol → production wrapper`
104/// map (affine / clamp / powi × 4 dtypes + powi_backward × 4 = 16 kernels).
105/// Contract: `fuel-dispatch/kernel-contracts/cpu/affine-clamp-powi.fkc.md`. The scalar
106/// params (affine mul/add, clamp min/max, powi exp) ride in `OpParams`, NOT the
107/// dtype-list, so the binding keys stay `[t, t]` for the single-input forward
108/// ops and `[t, t, t]` for the two-input `powi_backward`. The `ep!` symbol is
109/// built from `$op`/`$dt`, so the three f32 hand-written wrappers whose fn-name
110/// differs from the symbol (`clamp_elementwise_f32`, `powi_elementwise_f32`)
111/// still map to the contract's `clamp_f32` / `powi_f32` entry points.
112pub static CPU_AFFINE_CLAMP_POWI_ENTRY_POINTS: &[(&str, KernelRef)] = &[
113 // affine (y = mul*x + add)
114 ep!("affine", "f32", affine_f32_cpu_wrapper),
115 ep!("affine", "f64", affine_f64_cpu_wrapper),
116 ep!("affine", "bf16", affine_bf16_cpu_wrapper),
117 ep!("affine", "f16", affine_f16_cpu_wrapper),
118 // clamp (y = clamp(x, min, max))
119 ep!("clamp", "f32", clamp_elementwise_f32_cpu_wrapper),
120 ep!("clamp", "f64", clamp_f64_cpu_wrapper),
121 ep!("clamp", "bf16", clamp_bf16_cpu_wrapper),
122 ep!("clamp", "f16", clamp_f16_cpu_wrapper),
123 // powi (y = x.powi(exp))
124 ep!("powi", "f32", powi_elementwise_f32_cpu_wrapper),
125 ep!("powi", "f64", powi_f64_cpu_wrapper),
126 ep!("powi", "bf16", powi_bf16_cpu_wrapper),
127 ep!("powi", "f16", powi_f16_cpu_wrapper),
128 // powi_backward (grad_x = exp*x^(exp-1)*upstream) — TWO inputs (x, upstream)
129 ep!("powi_backward", "f32", powi_backward_f32_cpu_wrapper),
130 ep!("powi_backward", "f64", powi_backward_f64_cpu_wrapper),
131 ep!("powi_backward", "bf16", powi_backward_bf16_cpu_wrapper),
132 ep!("powi_backward", "f16", powi_backward_f16_cpu_wrapper),
133];
134
135/// The CPU elementwise-unary family's `symbol → production wrapper` map
136/// (22 ops × 4 dtypes = 88 kernels). Contract:
137/// `fuel-dispatch/kernel-contracts/cpu/elementwise-unary.fkc.md`. Each per-op section
138/// declares a BASE `entry_point` (e.g. `…::relu`) and enumerates
139/// `dtypes: [F32,F64,BF16,F16]`; the importer's §3.4 multi-dtype fan-out then
140/// resolves `<base>_<dtype>` (e.g. `relu_f32`) against this table — so the
141/// `$op` literals below are the byte-kernel BASES, NOT the OpKind names. The
142/// two GELU flavors stay distinct, and since S13 their OpKind names no
143/// longer match their (unrenamed) byte-kernel bases: `OpKind::GeluTanhElementwise`
144/// (the canonical tanh GELU) has base `gelu` (wrapper `gelu_elementwise_<dt>`),
145/// while `OpKind::GeluElementwise` (bare `Gelu` means erf since S13) has base
146/// `gelu_erf` (wrapper `gelu_erf_elementwise_<dt>`). The `unary` chassis
147/// umbrella is `registrable: false` (§3.10 describe-only) and never resolves,
148/// so it is absent here.
149pub static CPU_UNARY_ENTRY_POINTS: &[(&str, KernelRef)] = &[
150 ep!("relu", "f32", relu_elementwise_f32_cpu_wrapper),
151 ep!("relu", "f64", relu_elementwise_f64_cpu_wrapper),
152 ep!("relu", "bf16", relu_elementwise_bf16_cpu_wrapper),
153 ep!("relu", "f16", relu_elementwise_f16_cpu_wrapper),
154 ep!("neg", "f32", neg_elementwise_f32_cpu_wrapper),
155 ep!("neg", "f64", neg_elementwise_f64_cpu_wrapper),
156 ep!("neg", "bf16", neg_elementwise_bf16_cpu_wrapper),
157 ep!("neg", "f16", neg_elementwise_f16_cpu_wrapper),
158 ep!("sqr", "f32", sqr_elementwise_f32_cpu_wrapper),
159 ep!("sqr", "f64", sqr_elementwise_f64_cpu_wrapper),
160 ep!("sqr", "bf16", sqr_elementwise_bf16_cpu_wrapper),
161 ep!("sqr", "f16", sqr_elementwise_f16_cpu_wrapper),
162 ep!("sqrt", "f32", sqrt_elementwise_f32_cpu_wrapper),
163 ep!("sqrt", "f64", sqrt_elementwise_f64_cpu_wrapper),
164 ep!("sqrt", "bf16", sqrt_elementwise_bf16_cpu_wrapper),
165 ep!("sqrt", "f16", sqrt_elementwise_f16_cpu_wrapper),
166 ep!("recip", "f32", recip_elementwise_f32_cpu_wrapper),
167 ep!("recip", "f64", recip_elementwise_f64_cpu_wrapper),
168 ep!("recip", "bf16", recip_elementwise_bf16_cpu_wrapper),
169 ep!("recip", "f16", recip_elementwise_f16_cpu_wrapper),
170 ep!("abs", "f32", abs_elementwise_f32_cpu_wrapper),
171 ep!("abs", "f64", abs_elementwise_f64_cpu_wrapper),
172 ep!("abs", "bf16", abs_elementwise_bf16_cpu_wrapper),
173 ep!("abs", "f16", abs_elementwise_f16_cpu_wrapper),
174 ep!("tanh", "f32", tanh_elementwise_f32_cpu_wrapper),
175 ep!("tanh", "f64", tanh_elementwise_f64_cpu_wrapper),
176 ep!("tanh", "bf16", tanh_elementwise_bf16_cpu_wrapper),
177 ep!("tanh", "f16", tanh_elementwise_f16_cpu_wrapper),
178 ep!("exp", "f32", exp_elementwise_f32_cpu_wrapper),
179 ep!("exp", "f64", exp_elementwise_f64_cpu_wrapper),
180 ep!("exp", "bf16", exp_elementwise_bf16_cpu_wrapper),
181 ep!("exp", "f16", exp_elementwise_f16_cpu_wrapper),
182 ep!("log", "f32", log_elementwise_f32_cpu_wrapper),
183 ep!("log", "f64", log_elementwise_f64_cpu_wrapper),
184 ep!("log", "bf16", log_elementwise_bf16_cpu_wrapper),
185 ep!("log", "f16", log_elementwise_f16_cpu_wrapper),
186 ep!("sin", "f32", sin_elementwise_f32_cpu_wrapper),
187 ep!("sin", "f64", sin_elementwise_f64_cpu_wrapper),
188 ep!("sin", "bf16", sin_elementwise_bf16_cpu_wrapper),
189 ep!("sin", "f16", sin_elementwise_f16_cpu_wrapper),
190 ep!("cos", "f32", cos_elementwise_f32_cpu_wrapper),
191 ep!("cos", "f64", cos_elementwise_f64_cpu_wrapper),
192 ep!("cos", "bf16", cos_elementwise_bf16_cpu_wrapper),
193 ep!("cos", "f16", cos_elementwise_f16_cpu_wrapper),
194 ep!("sigmoid", "f32", sigmoid_elementwise_f32_cpu_wrapper),
195 ep!("sigmoid", "f64", sigmoid_elementwise_f64_cpu_wrapper),
196 ep!("sigmoid", "bf16", sigmoid_elementwise_bf16_cpu_wrapper),
197 ep!("sigmoid", "f16", sigmoid_elementwise_f16_cpu_wrapper),
198 ep!("silu", "f32", silu_elementwise_f32_cpu_wrapper),
199 ep!("silu", "f64", silu_elementwise_f64_cpu_wrapper),
200 ep!("silu", "bf16", silu_elementwise_bf16_cpu_wrapper),
201 ep!("silu", "f16", silu_elementwise_f16_cpu_wrapper),
202 ep!("step", "f32", step_elementwise_f32_cpu_wrapper),
203 ep!("step", "f64", step_elementwise_f64_cpu_wrapper),
204 ep!("step", "bf16", step_elementwise_bf16_cpu_wrapper),
205 ep!("step", "f16", step_elementwise_f16_cpu_wrapper),
206 // gelu_tanh (OpKind::GeluTanhElementwise, tanh approx): base `gelu`
207 // (matches the real byte_kernels::gelu_* symbol -- NOT renamed by S13,
208 // only the Op-level vocabulary was), wrapper `gelu_elementwise_*`.
209 ep!("gelu", "f32", gelu_elementwise_f32_cpu_wrapper),
210 ep!("gelu", "f64", gelu_elementwise_f64_cpu_wrapper),
211 ep!("gelu", "bf16", gelu_elementwise_bf16_cpu_wrapper),
212 ep!("gelu", "f16", gelu_elementwise_f16_cpu_wrapper),
213 ep!("floor", "f32", floor_elementwise_f32_cpu_wrapper),
214 ep!("floor", "f64", floor_elementwise_f64_cpu_wrapper),
215 ep!("floor", "bf16", floor_elementwise_bf16_cpu_wrapper),
216 ep!("floor", "f16", floor_elementwise_f16_cpu_wrapper),
217 ep!("ceil", "f32", ceil_elementwise_f32_cpu_wrapper),
218 ep!("ceil", "f64", ceil_elementwise_f64_cpu_wrapper),
219 ep!("ceil", "bf16", ceil_elementwise_bf16_cpu_wrapper),
220 ep!("ceil", "f16", ceil_elementwise_f16_cpu_wrapper),
221 ep!("round", "f32", round_elementwise_f32_cpu_wrapper),
222 ep!("round", "f64", round_elementwise_f64_cpu_wrapper),
223 ep!("round", "bf16", round_elementwise_bf16_cpu_wrapper),
224 ep!("round", "f16", round_elementwise_f16_cpu_wrapper),
225 ep!("sign", "f32", sign_elementwise_f32_cpu_wrapper),
226 ep!("sign", "f64", sign_elementwise_f64_cpu_wrapper),
227 ep!("sign", "bf16", sign_elementwise_bf16_cpu_wrapper),
228 ep!("sign", "f16", sign_elementwise_f16_cpu_wrapper),
229 ep!("erf", "f32", erf_elementwise_f32_cpu_wrapper),
230 ep!("erf", "f64", erf_elementwise_f64_cpu_wrapper),
231 ep!("erf", "bf16", erf_elementwise_bf16_cpu_wrapper),
232 ep!("erf", "f16", erf_elementwise_f16_cpu_wrapper),
233 // gelu (OpKind::GeluElementwise, exact erf): base `gelu_erf` (matches the
234 // real byte_kernels::gelu_erf_* symbol, unrenamed), DISTINCT from the
235 // tanh block above.
236 ep!("gelu_erf", "f32", gelu_erf_elementwise_f32_cpu_wrapper),
237 ep!("gelu_erf", "f64", gelu_erf_elementwise_f64_cpu_wrapper),
238 ep!("gelu_erf", "bf16", gelu_erf_elementwise_bf16_cpu_wrapper),
239 ep!("gelu_erf", "f16", gelu_erf_elementwise_f16_cpu_wrapper),
240 ep!("rsqrt", "f32", rsqrt_elementwise_f32_cpu_wrapper),
241 ep!("rsqrt", "f64", rsqrt_elementwise_f64_cpu_wrapper),
242 ep!("rsqrt", "bf16", rsqrt_elementwise_bf16_cpu_wrapper),
243 ep!("rsqrt", "f16", rsqrt_elementwise_f16_cpu_wrapper),
244];
245
246/// The CPU elementwise-COMPARE family's `symbol → production wrapper` map
247/// (6 ops × 4 dtypes = 24). Contract:
248/// `fuel-dispatch/kernel-contracts/cpu/compare-where.fkc.md`. Each thunk is a
249/// single-(op,dtype) section (its `lhs`/`rhs` enumerate ONE dtype), so it does
250/// NOT fan — the importer resolves its declared symbol AS-IS. That symbol
251/// carries the `_u8` output-mask suffix (`T × T → U8` mask, `return.out:
252/// fixed(U8)`), so the `ep!` dtype slot is `<dt>_u8` (e.g. `eq_f32_u8`), NOT
253/// the plain `<dt>` the binary/unary families use. The `## compare` chassis
254/// umbrella is `registrable: false` (§3.10 describe-only) and never resolves,
255/// so it is absent here.
256pub static CPU_COMPARE_ENTRY_POINTS: &[(&str, KernelRef)] = &[
257 ep!("eq", "f32_u8", eq_elementwise_f32_cpu_wrapper),
258 ep!("eq", "f64_u8", eq_elementwise_f64_cpu_wrapper),
259 ep!("eq", "bf16_u8", eq_elementwise_bf16_cpu_wrapper),
260 ep!("eq", "f16_u8", eq_elementwise_f16_cpu_wrapper),
261 ep!("ne", "f32_u8", ne_elementwise_f32_cpu_wrapper),
262 ep!("ne", "f64_u8", ne_elementwise_f64_cpu_wrapper),
263 ep!("ne", "bf16_u8", ne_elementwise_bf16_cpu_wrapper),
264 ep!("ne", "f16_u8", ne_elementwise_f16_cpu_wrapper),
265 ep!("lt", "f32_u8", lt_elementwise_f32_cpu_wrapper),
266 ep!("lt", "f64_u8", lt_elementwise_f64_cpu_wrapper),
267 ep!("lt", "bf16_u8", lt_elementwise_bf16_cpu_wrapper),
268 ep!("lt", "f16_u8", lt_elementwise_f16_cpu_wrapper),
269 ep!("le", "f32_u8", le_elementwise_f32_cpu_wrapper),
270 ep!("le", "f64_u8", le_elementwise_f64_cpu_wrapper),
271 ep!("le", "bf16_u8", le_elementwise_bf16_cpu_wrapper),
272 ep!("le", "f16_u8", le_elementwise_f16_cpu_wrapper),
273 ep!("gt", "f32_u8", gt_elementwise_f32_cpu_wrapper),
274 ep!("gt", "f64_u8", gt_elementwise_f64_cpu_wrapper),
275 ep!("gt", "bf16_u8", gt_elementwise_bf16_cpu_wrapper),
276 ep!("gt", "f16_u8", gt_elementwise_f16_cpu_wrapper),
277 ep!("ge", "f32_u8", ge_elementwise_f32_cpu_wrapper),
278 ep!("ge", "f64_u8", ge_elementwise_f64_cpu_wrapper),
279 ep!("ge", "bf16_u8", ge_elementwise_bf16_cpu_wrapper),
280 ep!("ge", "f16_u8", ge_elementwise_f16_cpu_wrapper),
281];
282
283/// The CPU ternary-select (`where`) family's `symbol → production wrapper` map
284/// (1 op × 4 dtypes = 4). Contract:
285/// `fuel-dispatch/kernel-contracts/cpu/compare-where.fkc.md`. The single `where_kernel`
286/// section enumerates `a`/`b` `dtypes: [F32,F64,BF16,F16]`, so it FANS (§3.4):
287/// its declared BASE `entry_point` `…::where` resolves `<base>_<dtype>` =
288/// `where_{f32,f64,bf16,f16}` against this table. The binding key is
289/// `[U8, T, T, T]` (cond U8 + a/b/out share T; `out: passthrough(a)` → T).
290pub static CPU_WHERE_ENTRY_POINTS: &[(&str, KernelRef)] = &[
291 ep!("where", "f32", where_f32_cpu_wrapper),
292 ep!("where", "f64", where_f64_cpu_wrapper),
293 ep!("where", "bf16", where_bf16_cpu_wrapper),
294 ep!("where", "f16", where_f16_cpu_wrapper),
295];
296
297/// The CPU per-axis REDUCE family's `symbol → production wrapper` map
298/// (4 ops × 4 dtypes = 16). Contract: `fuel-dispatch/kernel-contracts/cpu/reduce.fkc.md`.
299/// Each per-(op, dtype) section (`## sum_reduce_f32`, …) declares a SPECIFIC
300/// single-dtype `entry_point` (`…::sum_reduce_f32`), so it does NOT fan — the
301/// importer resolves that symbol AS-IS. The binding key is `[T, T]` (input +
302/// `passthrough(input)` output; the reduce axes + keepdim ride in
303/// `OpParams::Reduce`, NOT the dtype-list). The `## reduce` chassis umbrella is
304/// `registrable: false` (§3.10 describe-only) and never resolves, so it is
305/// absent here; the f32-only `argmax_dim_f32` / `argmin_dim_f32` sections are
306/// `registrable: false` (deferred — production registers Arg{Max,Min}Dim for all
307/// input dtypes via a hand-written dispatch) and are likewise absent.
308pub static CPU_REDUCE_ENTRY_POINTS: &[(&str, KernelRef)] = &[
309 ep!("sum_reduce", "f32", sum_reduce_f32_cpu_wrapper),
310 ep!("sum_reduce", "f64", sum_reduce_f64_cpu_wrapper),
311 ep!("sum_reduce", "bf16", sum_reduce_bf16_cpu_wrapper),
312 ep!("sum_reduce", "f16", sum_reduce_f16_cpu_wrapper),
313 ep!("mean_reduce", "f32", mean_reduce_f32_cpu_wrapper),
314 ep!("mean_reduce", "f64", mean_reduce_f64_cpu_wrapper),
315 ep!("mean_reduce", "bf16", mean_reduce_bf16_cpu_wrapper),
316 ep!("mean_reduce", "f16", mean_reduce_f16_cpu_wrapper),
317 ep!("max_reduce", "f32", max_reduce_f32_cpu_wrapper),
318 ep!("max_reduce", "f64", max_reduce_f64_cpu_wrapper),
319 ep!("max_reduce", "bf16", max_reduce_bf16_cpu_wrapper),
320 ep!("max_reduce", "f16", max_reduce_f16_cpu_wrapper),
321 ep!("min_reduce", "f32", min_reduce_f32_cpu_wrapper),
322 ep!("min_reduce", "f64", min_reduce_f64_cpu_wrapper),
323 ep!("min_reduce", "bf16", min_reduce_bf16_cpu_wrapper),
324 ep!("min_reduce", "f16", min_reduce_f16_cpu_wrapper),
325];
326
327/// The CPU broadcast-target REDUCE-TO family's `symbol → production wrapper`
328/// map (ReduceSumTo / ReduceMaxTo × 4 dtypes = 8, key `[T, T]`, +
329/// ReduceMaxToBackward × 4 dtypes = 4, key `[T, T, T]` = 12). Contract:
330/// `fuel-dispatch/kernel-contracts/cpu/reduce-to.fkc.md`. Each per-(op, dtype) section
331/// (`## reduce_sum_to_f32`, …) declares a SPECIFIC single-dtype `entry_point`
332/// (`…::reduce_sum_to_f32`), so it does NOT fan — the importer resolves that
333/// symbol AS-IS. The forward keys are `[T, T]` (input + `passthrough(input)`
334/// output; the target `input_shape`/`output_shape` ride in
335/// `OpParams::ReduceSumTo` / `OpParams::ReduceMaxTo`, NOT the dtype-list); the
336/// backward key is `[T, T, T]` (x, upstream + `passthrough(x)` output). The
337/// `## reduce_to` chassis umbrella is `registrable: false` (§3.10 describe-only)
338/// and never resolves, so it is absent here (without it the chassis would
339/// double-register `ReduceSumTo`/`[F32]` → `DuplicateKernelRef` at init).
340pub static CPU_REDUCE_TO_ENTRY_POINTS: &[(&str, KernelRef)] = &[
341 ep!("reduce_sum_to", "f32", reduce_sum_to_f32_cpu_wrapper),
342 ep!("reduce_sum_to", "f64", reduce_sum_to_f64_cpu_wrapper),
343 ep!("reduce_sum_to", "bf16", reduce_sum_to_bf16_cpu_wrapper),
344 ep!("reduce_sum_to", "f16", reduce_sum_to_f16_cpu_wrapper),
345 ep!("reduce_max_to", "f32", reduce_max_to_f32_cpu_wrapper),
346 ep!("reduce_max_to", "f64", reduce_max_to_f64_cpu_wrapper),
347 ep!("reduce_max_to", "bf16", reduce_max_to_bf16_cpu_wrapper),
348 ep!("reduce_max_to", "f16", reduce_max_to_f16_cpu_wrapper),
349 ep!(
350 "reduce_max_to_backward",
351 "f32",
352 reduce_max_to_backward_f32_cpu_wrapper
353 ),
354 ep!(
355 "reduce_max_to_backward",
356 "f64",
357 reduce_max_to_backward_f64_cpu_wrapper
358 ),
359 ep!(
360 "reduce_max_to_backward",
361 "bf16",
362 reduce_max_to_backward_bf16_cpu_wrapper
363 ),
364 ep!(
365 "reduce_max_to_backward",
366 "f16",
367 reduce_max_to_backward_f16_cpu_wrapper
368 ),
369];
370
371/// The CPU last-dim NORM (forward) family's `symbol → production wrapper` map
372/// (Softmax / LogSoftmax / RmsNorm / LayerNorm last-dim × 4 dtypes = 16). Contract:
373/// `fuel-dispatch/kernel-contracts/cpu/norm.fkc.md`. Each per-(op, dtype) section
374/// (`## softmax_last_dim_f32`, `## rms_norm_last_dim_f32`, …) declares a SPECIFIC
375/// single-dtype `entry_point` (`…::softmax_last_dim_f32`), so none of them fan —
376/// the importer resolves that symbol AS-IS. The binding key is `[T, T]` (a SINGLE
377/// input + `passthrough(input)` output; the RMS/LayerNorm kernels carry NO affine
378/// gamma/beta operand — they are the bare normalization — and `outer_count` /
379/// `last_dim` / `eps` ride in `OpParams::{SoftmaxLastDim,LogSoftmaxLastDim,
380/// NormLastDim}`, NOT the dtype-list), identical to the deleted `&unary(t)` regs.
381/// The `log_softmax` wrapper fn-names (`log_softmax_<dt>_cpu_wrapper`) differ from
382/// their `log_softmax_last_dim_<dt>` symbol — the `ep!` symbol is built from the
383/// `$op`/`$dt` literals, so the mapping still binds the correct contract symbol
384/// (mirrors the clamp/powi/where fn-vs-symbol cases). This contract has NO `##`
385/// chassis umbrella section, so there is no `registrable: false` describe-only
386/// entry to omit; the BACKWARD forms live in a separate norm-backward contract.
387pub static CPU_NORM_ENTRY_POINTS: &[(&str, KernelRef)] = &[
388 ep!("softmax_last_dim", "f32", softmax_last_dim_f32_cpu_wrapper),
389 ep!("softmax_last_dim", "f64", softmax_last_dim_f64_cpu_wrapper),
390 ep!(
391 "softmax_last_dim",
392 "bf16",
393 softmax_last_dim_bf16_cpu_wrapper
394 ),
395 ep!("softmax_last_dim", "f16", softmax_last_dim_f16_cpu_wrapper),
396 ep!("log_softmax_last_dim", "f32", log_softmax_f32_cpu_wrapper),
397 ep!("log_softmax_last_dim", "f64", log_softmax_f64_cpu_wrapper),
398 ep!("log_softmax_last_dim", "bf16", log_softmax_bf16_cpu_wrapper),
399 ep!("log_softmax_last_dim", "f16", log_softmax_f16_cpu_wrapper),
400 ep!(
401 "rms_norm_last_dim",
402 "f32",
403 rms_norm_last_dim_f32_cpu_wrapper
404 ),
405 ep!(
406 "rms_norm_last_dim",
407 "f64",
408 rms_norm_last_dim_f64_cpu_wrapper
409 ),
410 ep!(
411 "rms_norm_last_dim",
412 "bf16",
413 rms_norm_last_dim_bf16_cpu_wrapper
414 ),
415 ep!(
416 "rms_norm_last_dim",
417 "f16",
418 rms_norm_last_dim_f16_cpu_wrapper
419 ),
420 ep!(
421 "layer_norm_last_dim",
422 "f32",
423 layer_norm_last_dim_f32_cpu_wrapper
424 ),
425 ep!(
426 "layer_norm_last_dim",
427 "f64",
428 layer_norm_last_dim_f64_cpu_wrapper
429 ),
430 ep!(
431 "layer_norm_last_dim",
432 "bf16",
433 layer_norm_last_dim_bf16_cpu_wrapper
434 ),
435 ep!(
436 "layer_norm_last_dim",
437 "f16",
438 layer_norm_last_dim_f16_cpu_wrapper
439 ),
440];
441
442/// The CPU last-dim NORM-BACKWARD family's `symbol → production wrapper` map
443/// (Softmax / LogSoftmax / RmsNorm / LayerNorm last-dim BACKWARD × 4 dtypes = 16).
444/// Contract: `fuel-dispatch/kernel-contracts/cpu/norm-backward.fkc.md`. Each per-(op, dtype)
445/// section (`## softmax_last_dim_backward_f32`, `## rms_norm_last_dim_backward_f32`,
446/// …) declares a SPECIFIC single-dtype `entry_point`
447/// (`…::softmax_last_dim_backward_f32`), so none of them fan — the importer
448/// resolves that symbol AS-IS. The binding key is `[T, T, T]` — the BARE backward
449/// takes TWO inputs (softmax/log-softmax: the forward output `y` + the upstream
450/// gradient `g`; layer/rms-norm: the forward input `x` + `g`, stats recomputed) and
451/// writes ONE `passthrough(y|x)` output, and outer_count / last_dim / eps ride in
452/// `OpParams::{SoftmaxLastDim,LogSoftmaxLastDim,NormLastDim}`, NOT the dtype-list —
453/// identical to the deleted `&binary(t)` regs. The `log_softmax` backward wrapper
454/// fn-names (`log_softmax_backward_<dt>_cpu_wrapper`) differ from their
455/// `log_softmax_last_dim_backward_<dt>` symbol — the `ep!` symbol is built from the
456/// `$op`/`$dt` literals, so the mapping still binds the correct contract symbol
457/// (mirrors the forward `log_softmax` fn-vs-symbol case). This contract has NO `##`
458/// chassis umbrella section, so there is no `registrable: false` describe-only entry
459/// to omit; the FORWARD forms live in the separate norm contract.
460pub static CPU_NORM_BACKWARD_ENTRY_POINTS: &[(&str, KernelRef)] = &[
461 ep!(
462 "softmax_last_dim_backward",
463 "f32",
464 softmax_last_dim_backward_f32_cpu_wrapper
465 ),
466 ep!(
467 "softmax_last_dim_backward",
468 "f64",
469 softmax_last_dim_backward_f64_cpu_wrapper
470 ),
471 ep!(
472 "softmax_last_dim_backward",
473 "bf16",
474 softmax_last_dim_backward_bf16_cpu_wrapper
475 ),
476 ep!(
477 "softmax_last_dim_backward",
478 "f16",
479 softmax_last_dim_backward_f16_cpu_wrapper
480 ),
481 ep!(
482 "log_softmax_last_dim_backward",
483 "f32",
484 log_softmax_backward_f32_cpu_wrapper
485 ),
486 ep!(
487 "log_softmax_last_dim_backward",
488 "f64",
489 log_softmax_backward_f64_cpu_wrapper
490 ),
491 ep!(
492 "log_softmax_last_dim_backward",
493 "bf16",
494 log_softmax_backward_bf16_cpu_wrapper
495 ),
496 ep!(
497 "log_softmax_last_dim_backward",
498 "f16",
499 log_softmax_backward_f16_cpu_wrapper
500 ),
501 ep!(
502 "rms_norm_last_dim_backward",
503 "f32",
504 rms_norm_last_dim_backward_f32_cpu_wrapper
505 ),
506 ep!(
507 "rms_norm_last_dim_backward",
508 "f64",
509 rms_norm_last_dim_backward_f64_cpu_wrapper
510 ),
511 ep!(
512 "rms_norm_last_dim_backward",
513 "bf16",
514 rms_norm_last_dim_backward_bf16_cpu_wrapper
515 ),
516 ep!(
517 "rms_norm_last_dim_backward",
518 "f16",
519 rms_norm_last_dim_backward_f16_cpu_wrapper
520 ),
521 ep!(
522 "layer_norm_last_dim_backward",
523 "f32",
524 layer_norm_last_dim_backward_f32_cpu_wrapper
525 ),
526 ep!(
527 "layer_norm_last_dim_backward",
528 "f64",
529 layer_norm_last_dim_backward_f64_cpu_wrapper
530 ),
531 ep!(
532 "layer_norm_last_dim_backward",
533 "bf16",
534 layer_norm_last_dim_backward_bf16_cpu_wrapper
535 ),
536 ep!(
537 "layer_norm_last_dim_backward",
538 "f16",
539 layer_norm_last_dim_backward_f16_cpu_wrapper
540 ),
541];
542
543/// The CPU RoPE (rotary position embedding) family's `symbol → production
544/// wrapper` map (1 op × 4 dtypes = 4). Contract:
545/// `fuel-dispatch/kernel-contracts/cpu/rope.fkc.md`. Each per-dtype section
546/// (`## rope_f32`, …) declares a SPECIFIC single-dtype `entry_point`
547/// (`…::rope_f32`), so none of them fan — the importer resolves that symbol
548/// AS-IS. The binding key is `[T, T, T, T]` — RoPE takes THREE inputs (`x` +
549/// the precomputed `cos`/`sin` tables, all one dtype; the `[seq, head_dim]`
550/// tables broadcast over the `outer_count` axis by the kernel re-indexing them
551/// per outer, NOT a stride-0 view) and writes ONE `passthrough(x)` output;
552/// outer_count / seq / head_dim ride in `OpParams::Rope`, NOT the dtype-list —
553/// identical to the deleted `rope_dts(t)` regs. This contract has NO `##`
554/// chassis umbrella section, so there is no `registrable: false` describe-only
555/// entry to omit. RoPE is ALSO registered in the `FusedKernelRegistry`
556/// (`register_default_fused_kernels`, `FusedOps::ROPE`) — that is a SEPARATE
557/// registry seam and stays untouched; this map only serves the
558/// `KernelBindingTable` primitive path.
559pub static CPU_ROPE_ENTRY_POINTS: &[(&str, KernelRef)] = &[
560 ep!("rope", "f32", rope_f32_cpu_wrapper),
561 ep!("rope", "f64", rope_f64_cpu_wrapper),
562 ep!("rope", "bf16", rope_bf16_cpu_wrapper),
563 ep!("rope", "f16", rope_f16_cpu_wrapper),
564];
565
566/// The CPU SSM / Mamba family's `symbol → production wrapper` map — the FULL
567/// family: FusedSoftmaxCrossEntropy + CausalConv1d + SelectiveScan +
568/// SsdChunkScan (4 ops × 4 dtypes = 16). Contract:
569/// `fuel-dispatch/kernel-contracts/cpu/ssm.fkc.md`. Each per-(op, dtype) section
570/// (`## fused_softmax_cross_entropy_f32`, `## selective_scan_f32`, …) declares a
571/// SPECIFIC single-dtype `entry_point` (`…::selective_scan_f32`), so none of
572/// them fan — the importer resolves that symbol AS-IS. Binding keys:
573/// - FSCE: `[T, I64, F32]` (logits T + I64 targets → `fixed(F32)` output;
574/// n_rows / vocab / reduction / ignore_index ride in
575/// `OpParams::FusedSoftmaxCrossEntropy`, NOT the dtype-list).
576/// - CausalConv1d: `[T, T, T, T]` (x, weight, bias + `passthrough(x)` output;
577/// batch / channels / seq / kernel / use_silu ride in `OpParams::CausalConv1d`).
578/// - SelectiveScan / SsdChunkScan: `[T; 6]` — 5 inputs (u,delta,a,b,c /
579/// x,dt,a,b,c) + the ONE bundled output slot. These sections return a
580/// `return.bundle` multi-output (Option C, one packed buffer `[y ; last_state]`);
581/// the importer's key-builder now appends the bundle's PRIMARY-slot dtype
582/// (`passthrough(u)` / `passthrough(x)` → T) to the key tail (`fkc/lower.rs`
583/// `assemble_dtype_variants`), so a bundle section keys `[T; 6]` byte-for-byte
584/// the deleted hand-written reg. The geometry / delta_softplus / chunk_size
585/// ride in `OpParams::{SelectiveScan, SsdChunkScan}`, NOT the dtype-list.
586///
587/// These sections have NO `##` chassis umbrella, so there is no
588/// `registrable: false` describe-only entry to omit.
589pub static CPU_SSM_ENTRY_POINTS: &[(&str, KernelRef)] = &[
590 ep!(
591 "fused_softmax_cross_entropy",
592 "f32",
593 fused_softmax_cross_entropy_f32_cpu_wrapper
594 ),
595 ep!(
596 "fused_softmax_cross_entropy",
597 "f64",
598 fused_softmax_cross_entropy_f64_cpu_wrapper
599 ),
600 ep!(
601 "fused_softmax_cross_entropy",
602 "bf16",
603 fused_softmax_cross_entropy_bf16_cpu_wrapper
604 ),
605 ep!(
606 "fused_softmax_cross_entropy",
607 "f16",
608 fused_softmax_cross_entropy_f16_cpu_wrapper
609 ),
610 ep!("causal_conv1d", "f32", causal_conv1d_f32_cpu_wrapper),
611 ep!("causal_conv1d", "f64", causal_conv1d_f64_cpu_wrapper),
612 ep!("causal_conv1d", "bf16", causal_conv1d_bf16_cpu_wrapper),
613 ep!("causal_conv1d", "f16", causal_conv1d_f16_cpu_wrapper),
614 ep!("selective_scan", "f32", selective_scan_f32_cpu_wrapper),
615 ep!("selective_scan", "f64", selective_scan_f64_cpu_wrapper),
616 ep!("selective_scan", "bf16", selective_scan_bf16_cpu_wrapper),
617 ep!("selective_scan", "f16", selective_scan_f16_cpu_wrapper),
618 ep!("ssd_chunk_scan", "f32", ssd_chunk_scan_f32_cpu_wrapper),
619 ep!("ssd_chunk_scan", "f64", ssd_chunk_scan_f64_cpu_wrapper),
620 ep!("ssd_chunk_scan", "bf16", ssd_chunk_scan_bf16_cpu_wrapper),
621 ep!("ssd_chunk_scan", "f16", ssd_chunk_scan_f16_cpu_wrapper),
622];
623
624/// The CPU 2D-convolution family's `symbol → production wrapper` map — the FULL
625/// family: Conv2D + ConvTranspose2D × 4 dtypes = 8 symbols (each symbol serves
626/// BOTH operand-count keys). Contract: `fuel-dispatch/kernel-contracts/cpu/conv.fkc.md`.
627/// Each per-(op, dtype) section (`## conv2d_f32`, `## conv_transpose2d_f32`, …)
628/// declares a SPECIFIC single-dtype `entry_point` (`…::conv2d_f32`), so none of
629/// them dtype-fan — the importer resolves that symbol AS-IS. Because the contract
630/// marks `bias` as `optional: true`, the importer's key-builder fans each section
631/// into BOTH the no-bias key `[T, T, T]` (x, weight + out) and the with-bias key
632/// `[T, T, T, T]` (x, weight, bias + `passthrough(x)` output) — both resolving
633/// this SAME symbol/wrapper (the CPU wrapper handles 2 or 3 inputs). The spatial
634/// geometry (x_shape/w_shape/out_shape, stride/padding/`[output_padding]`/dilation,
635/// groups) rides in `OpParams::{Conv2D, ConvTranspose2D}`, NOT the dtype-list.
636/// This map is the SOLE registration path for the whole family: every
637/// hand-written `table.register(Conv2D/ConvTranspose2D, …)` reg (both operand
638/// counts) is DELETED.
639///
640/// The conv ops' SEPARATE `FusedKernelRegistry` seam
641/// (`register_default_fused_kernels`, `FusedOps::{CONV2D, CONV_TRANSPOSE2D}`,
642/// both no-bias + with-bias) is untouched; this map only serves the
643/// `KernelBindingTable` primitive path. These sections have NO `##` chassis
644/// umbrella, so there is no `registrable: false` describe-only entry to omit.
645pub static CPU_CONV_ENTRY_POINTS: &[(&str, KernelRef)] = &[
646 ep!("conv2d", "f32", conv2d_f32_cpu_wrapper),
647 ep!("conv2d", "f64", conv2d_f64_cpu_wrapper),
648 ep!("conv2d", "bf16", conv2d_bf16_cpu_wrapper),
649 ep!("conv2d", "f16", conv2d_f16_cpu_wrapper),
650 ep!("conv_transpose2d", "f32", conv_transpose2d_f32_cpu_wrapper),
651 ep!("conv_transpose2d", "f64", conv_transpose2d_f64_cpu_wrapper),
652 ep!(
653 "conv_transpose2d",
654 "bf16",
655 conv_transpose2d_bf16_cpu_wrapper
656 ),
657 ep!("conv_transpose2d", "f16", conv_transpose2d_f16_cpu_wrapper),
658];
659
660/// The CPU **padding** family's `symbol → production wrapper` map — the FULL
661/// family: mode-unified forward `Pad` × 6 dtypes = 6 + `PadBackward` × 4 dtypes
662/// = 4 (all key `[T, T]`). Contract: `fuel-dispatch/kernel-contracts/cpu/padding.fkc.md`.
663///
664/// - **Forward `Pad`** is a dtype-agnostic byte kernel with a SINGLE production
665/// wrapper — the mode-dispatching `pad_cpu_wrapper` (dispatch layer), which
666/// selects `pad_const_cpu` / `pad_reflect_cpu` / `pad_replicate_cpu` at runtime
667/// via `mode_tag` (incl. the reflect `before/after <= n-1` validation). The
668/// contract's ONE registrable `## pad` section declares a BASE `entry_point`
669/// (`…::pad_cpu`, a SYNTHETIC umbrella — there is no real `pad_cpu` byte kernel;
670/// the three real mode kernels are `pad_{const,reflect,replicate}_cpu`) + an
671/// enumerated `dtypes` list trimmed to production's wired 6
672/// (`U8/U32/BF16/F16/F32/F64`), so the importer's §3.4 multi-dtype fan-out
673/// resolves `pad_cpu_<dtype>` (a FABRICATED per-dtype symbol) — every dtype
674/// variant mapping to the ONE `pad_cpu_wrapper`, exactly the Flip/Roll/Concat
675/// pattern. The fan emits BYTE-FOR-BYTE the deleted hand-written
676/// `table.register(Pad, &unary(t), …)` regs (key `[T, T]`, `out:
677/// passthrough(input)`; the in_shape/out_shape/padding/mode_tag/fill_bytes ride
678/// in `OpParams::Pad`, NOT the dtype-list). The three per-mode sections
679/// (`pad_const_cpu` / `pad_reflect_cpu` / `pad_replicate_cpu`) and the
680/// `pad_walk_cpu` helper are `registrable: false` (§3.10 describe-only mode
681/// documentation) and never resolve, so they are ABSENT here.
682/// - **`PadBackward`** is per-dtype (typed gradient accumulation — bf16/f16/f32
683/// widen the scratch accumulator to f64, unlike the dtype-agnostic forward
684/// copy). Each per-dtype section (`## pad_backward_f32`, …) declares a SPECIFIC
685/// single-dtype `entry_point` (`…::pad_backward_f32`) resolved AS-IS (no fan),
686/// key `[T, T]` (grad_out + `passthrough(grad_out)` grad_in; in_shape/out_shape/
687/// padding/mode_tag ride in `OpParams::PadBackward`).
688pub static CPU_PADDING_ENTRY_POINTS: &[(&str, KernelRef)] = &[
689 // Forward Pad — mode-unified dtype-agnostic byte kernel; the fabricated
690 // `pad_cpu_<dt>` symbol maps to the ONE mode-dispatching `pad_cpu_wrapper`.
691 ep!("pad_cpu", "u8", pad_cpu_wrapper),
692 ep!("pad_cpu", "u32", pad_cpu_wrapper),
693 ep!("pad_cpu", "bf16", pad_cpu_wrapper),
694 ep!("pad_cpu", "f16", pad_cpu_wrapper),
695 ep!("pad_cpu", "f32", pad_cpu_wrapper),
696 ep!("pad_cpu", "f64", pad_cpu_wrapper),
697 // PadBackward — per-dtype typed accumulation; resolved AS-IS (no fan).
698 ep!("pad_backward", "f32", pad_backward_f32_cpu_wrapper),
699 ep!("pad_backward", "f64", pad_backward_f64_cpu_wrapper),
700 ep!("pad_backward", "bf16", pad_backward_bf16_cpu_wrapper),
701 ep!("pad_backward", "f16", pad_backward_f16_cpu_wrapper),
702];
703
704/// The CPU **shape-ops** family's `symbol → production wrapper` map — the
705/// MIGRATED subset (Flip + Roll + Concat + MaskedFill + WriteSlice +
706/// WriteSliceRotating dtype-fanned + CumSum per-dtype). Contract:
707/// `fuel-dispatch/kernel-contracts/cpu/shape-ops.fkc.md`.
708///
709/// - **Flip** / **Roll** / **Concat** / **MaskedFill** / **WriteSlice** /
710/// **WriteSliceRotating** are **dtype-agnostic byte kernels** with a SINGLE
711/// production wrapper each. Their contract sections declare a BASE
712/// `entry_point` (`…::flip_cpu`, `…::roll_cpu`, `…::concat_cpu`,
713/// `…::masked_fill_cpu`, `…::write_slice_cpu`, `…::write_slice_rotating_cpu`) +
714/// an enumerated `dtypes` list, so the importer's §3.4 multi-dtype fan-out
715/// resolves `<base>_<dtype>` (`flip_cpu_f32`, …) — a FABRICATED per-dtype
716/// symbol (there is no real `flip_cpu_f32` fn) that every dtype variant maps to
717/// the ONE dtype-agnostic wrapper. The contract's `dtypes` list was trimmed to
718/// production's wired set (Flip/Roll/MaskedFill/WriteSlice/WriteSliceRotating 6:
719/// F32/F64/BF16/F16/U32/U8; Concat 9: +I16/I32/I64) so the fan emits
720/// BYTE-FOR-BYTE the deleted hand-written regs — Flip/Roll/Concat/WriteSlice/
721/// WriteSliceRotating key `[T, T]`, MaskedFill key `[T, U8, T]` (`input` is the
722/// sole varying operand; `mask` stays the fixed U8 slot; `out:
723/// passthrough(input)`). **WriteSlice / WriteSliceRotating** model `dest` as the
724/// in-place OUTPUT slot (not a key input) and — for the rotating op — the U32
725/// `position` as a NON-KEY runtime operand, so the importer keys `[T_src, T_out]`
726/// = `[T, T]`, matching `build_lookup_dtypes`' canonicalization exactly.
727/// - **CumSum** is per-dtype (typed accumulation, NOT a byte copy — f32/f64
728/// native, bf16/f16 widen to an f32 accumulator), so each `cumsum_<dt>`
729/// section declares a SPECIFIC single-dtype `entry_point` resolved AS-IS (no
730/// fan), key `[T, T]`, mapping to its OWN typed wrapper.
731///
732/// DEFERRED (absent here — never resolved): `contiguize` and `triangular` are
733/// `registrable: false` chassis/describe-only sections (no `OpKind::Contiguize`;
734/// `triangular` is the umbrella backing the two hand-written `Triu`/`Tril`
735/// OpKinds, not a keyable section).
736pub static CPU_SHAPE_OPS_ENTRY_POINTS: &[(&str, KernelRef)] = &[
737 // Flip — dtype-agnostic byte reorder; one wrapper fanned per dtype.
738 ep!("flip_cpu", "f32", flip_cpu_wrapper),
739 ep!("flip_cpu", "f64", flip_cpu_wrapper),
740 ep!("flip_cpu", "bf16", flip_cpu_wrapper),
741 ep!("flip_cpu", "f16", flip_cpu_wrapper),
742 ep!("flip_cpu", "u32", flip_cpu_wrapper),
743 ep!("flip_cpu", "u8", flip_cpu_wrapper),
744 // Roll — dtype-agnostic cyclic shift.
745 ep!("roll_cpu", "f32", roll_cpu_wrapper),
746 ep!("roll_cpu", "f64", roll_cpu_wrapper),
747 ep!("roll_cpu", "bf16", roll_cpu_wrapper),
748 ep!("roll_cpu", "f16", roll_cpu_wrapper),
749 ep!("roll_cpu", "u32", roll_cpu_wrapper),
750 ep!("roll_cpu", "u8", roll_cpu_wrapper),
751 // Concat — variadic uniform-dtype join collapsed to the [T, T] shorthand.
752 ep!("concat_cpu", "f32", concat_cpu_wrapper),
753 ep!("concat_cpu", "f64", concat_cpu_wrapper),
754 ep!("concat_cpu", "bf16", concat_cpu_wrapper),
755 ep!("concat_cpu", "f16", concat_cpu_wrapper),
756 ep!("concat_cpu", "u32", concat_cpu_wrapper),
757 ep!("concat_cpu", "u8", concat_cpu_wrapper),
758 ep!("concat_cpu", "i16", concat_cpu_wrapper),
759 ep!("concat_cpu", "i32", concat_cpu_wrapper),
760 ep!("concat_cpu", "i64", concat_cpu_wrapper),
761 // MaskedFill — dtype-agnostic data + fixed U8 mask; key [T, U8, T].
762 ep!("masked_fill_cpu", "f32", masked_fill_cpu_wrapper),
763 ep!("masked_fill_cpu", "f64", masked_fill_cpu_wrapper),
764 ep!("masked_fill_cpu", "bf16", masked_fill_cpu_wrapper),
765 ep!("masked_fill_cpu", "f16", masked_fill_cpu_wrapper),
766 ep!("masked_fill_cpu", "u32", masked_fill_cpu_wrapper),
767 ep!("masked_fill_cpu", "u8", masked_fill_cpu_wrapper),
768 // CumSum — per-dtype typed kernels, resolved AS-IS (no fan).
769 ep!("cumsum", "f32", cumsum_f32_cpu_wrapper),
770 ep!("cumsum", "f64", cumsum_f64_cpu_wrapper),
771 ep!("cumsum", "bf16", cumsum_bf16_cpu_wrapper),
772 ep!("cumsum", "f16", cumsum_f16_cpu_wrapper),
773 // WriteSlice — in-place rectangular scatter; dtype-agnostic byte copy, one
774 // wrapper fanned per dtype. `dest` is the in-place OUTPUT slot, so the key is
775 // `[T_src, T_out]` = [T, T]; the fabricated `write_slice_cpu_<dt>` symbol maps
776 // to the ONE dtype-agnostic wrapper.
777 ep!("write_slice_cpu", "f32", write_slice_cpu_wrapper),
778 ep!("write_slice_cpu", "f64", write_slice_cpu_wrapper),
779 ep!("write_slice_cpu", "bf16", write_slice_cpu_wrapper),
780 ep!("write_slice_cpu", "f16", write_slice_cpu_wrapper),
781 ep!("write_slice_cpu", "u32", write_slice_cpu_wrapper),
782 ep!("write_slice_cpu", "u8", write_slice_cpu_wrapper),
783 // WriteSliceRotating — in-place ring-buffer scatter; same [T, T] key (dest =
784 // output slot; the runtime `position` U32 operand is NOT a key slot). One
785 // dtype-agnostic wrapper fanned per dtype.
786 ep!(
787 "write_slice_rotating_cpu",
788 "f32",
789 write_slice_rotating_cpu_wrapper
790 ),
791 ep!(
792 "write_slice_rotating_cpu",
793 "f64",
794 write_slice_rotating_cpu_wrapper
795 ),
796 ep!(
797 "write_slice_rotating_cpu",
798 "bf16",
799 write_slice_rotating_cpu_wrapper
800 ),
801 ep!(
802 "write_slice_rotating_cpu",
803 "f16",
804 write_slice_rotating_cpu_wrapper
805 ),
806 ep!(
807 "write_slice_rotating_cpu",
808 "u32",
809 write_slice_rotating_cpu_wrapper
810 ),
811 ep!(
812 "write_slice_rotating_cpu",
813 "u8",
814 write_slice_rotating_cpu_wrapper
815 ),
816 // WriteSliceDoff — in-place scatter with a device-resident I64 start on one
817 // axis (no wrap). Same [T, T] key (dest = output slot; the runtime I64
818 // `offset` operand is NOT a key slot). One dtype-agnostic wrapper per dtype.
819 ep!("write_slice_doff_cpu", "f32", write_slice_doff_cpu_wrapper),
820 ep!("write_slice_doff_cpu", "f64", write_slice_doff_cpu_wrapper),
821 ep!("write_slice_doff_cpu", "bf16", write_slice_doff_cpu_wrapper),
822 ep!("write_slice_doff_cpu", "f16", write_slice_doff_cpu_wrapper),
823 ep!("write_slice_doff_cpu", "u32", write_slice_doff_cpu_wrapper),
824 ep!("write_slice_doff_cpu", "u8", write_slice_doff_cpu_wrapper),
825];
826
827/// The CPU **indexing / gather / scatter** family's `symbol → production
828/// wrapper` map — the FULL family (IndexSelect + Gather dtype-fanned, IndexAdd +
829/// ScatterAdd per-dtype). Contract: `fuel-dispatch/kernel-contracts/cpu/indexing.fkc.md`.
830///
831/// - **IndexSelect** / **Gather** are **dtype-agnostic byte copies** (parameter-
832/// ized `dtype_size`, one copy per selected/gathered element) with a SINGLE
833/// production wrapper each. Their contract sections declare a BASE `entry_point`
834/// (`…::index_select_cpu` / `…::gather_cpu`) + an enumerated `dtypes` list, so
835/// the importer's §3.4 fan-out resolves `<base>_<dtype>` (`index_select_cpu_f32`,
836/// …) — a FABRICATED per-dtype symbol (there is no real `index_select_cpu_f32`
837/// fn) that every dtype variant maps to the ONE dtype-agnostic wrapper. The
838/// `indices` operand is the FIXED U32 slot and `out: passthrough(source)`, so
839/// the fan emits key `[T, U32, T]`. The contract's `dtypes` list was trimmed to
840/// production's 9 wired dtypes (F32/F64/BF16/F16/U32/U8/I16/I32/I64) — I8 is
841/// describable (byte-agnostic) but NOT wired — so the fan emits BYTE-FOR-BYTE
842/// the deleted hand-written `table.register(IndexSelect/Gather, …)` regs.
843/// - **IndexAdd** / **ScatterAdd** are **per-dtype** (typed accumulation — f32/f64
844/// native, bf16/f16 widen to an f32 accumulator; out seeded from `base` then
845/// `+= src`), so each `index_add_<dt>` / `scatter_add_<dt>` section declares a
846/// SPECIFIC single-dtype `entry_point` resolved AS-IS (no fan), key
847/// `[T, U32, T, T]` (`base`, U32 `indices`, `src`, `passthrough(base)` output),
848/// mapping to its OWN typed wrapper — 4 dtypes each (F32/F64/BF16/F16).
849pub static CPU_INDEXING_ENTRY_POINTS: &[(&str, KernelRef)] = &[
850 // IndexSelect — dtype-agnostic byte copy; the fabricated
851 // `index_select_cpu_<dt>` symbol maps to the ONE `index_select_cpu_wrapper`.
852 ep!("index_select_cpu", "f32", index_select_cpu_wrapper),
853 ep!("index_select_cpu", "f64", index_select_cpu_wrapper),
854 ep!("index_select_cpu", "bf16", index_select_cpu_wrapper),
855 ep!("index_select_cpu", "f16", index_select_cpu_wrapper),
856 ep!("index_select_cpu", "u32", index_select_cpu_wrapper),
857 ep!("index_select_cpu", "u8", index_select_cpu_wrapper),
858 ep!("index_select_cpu", "i16", index_select_cpu_wrapper),
859 ep!("index_select_cpu", "i32", index_select_cpu_wrapper),
860 ep!("index_select_cpu", "i64", index_select_cpu_wrapper),
861 // Gather — dtype-agnostic byte copy.
862 ep!("gather_cpu", "f32", gather_cpu_wrapper),
863 ep!("gather_cpu", "f64", gather_cpu_wrapper),
864 ep!("gather_cpu", "bf16", gather_cpu_wrapper),
865 ep!("gather_cpu", "f16", gather_cpu_wrapper),
866 ep!("gather_cpu", "u32", gather_cpu_wrapper),
867 ep!("gather_cpu", "u8", gather_cpu_wrapper),
868 ep!("gather_cpu", "i16", gather_cpu_wrapper),
869 ep!("gather_cpu", "i32", gather_cpu_wrapper),
870 ep!("gather_cpu", "i64", gather_cpu_wrapper),
871 // IndexAdd — per-dtype typed accumulation; resolved AS-IS (no fan).
872 ep!("index_add", "f32", index_add_f32_cpu_wrapper),
873 ep!("index_add", "f64", index_add_f64_cpu_wrapper),
874 ep!("index_add", "bf16", index_add_bf16_cpu_wrapper),
875 ep!("index_add", "f16", index_add_f16_cpu_wrapper),
876 // ScatterAdd — per-dtype typed accumulation; resolved AS-IS (no fan).
877 ep!("scatter_add", "f32", scatter_add_f32_cpu_wrapper),
878 ep!("scatter_add", "f64", scatter_add_f64_cpu_wrapper),
879 ep!("scatter_add", "bf16", scatter_add_bf16_cpu_wrapper),
880 ep!("scatter_add", "f16", scatter_add_f16_cpu_wrapper),
881];
882
883/// The CPU **matmul** family's `symbol → production wrapper` map — the FULL
884/// portable family: bare batched `MatMul` (6 dtypes) + fused `FusedLinear`
885/// (matmul + bias-add, 4 dtypes) = 10 entry points. Contract:
886/// `fuel-dispatch/kernel-contracts/cpu/matmul.fkc.md`.
887///
888/// Each per-(op, dtype) section (`## matmul_f32`, `## fused_linear_f32`, …)
889/// declares a SPECIFIC single-dtype `entry_point` (`…::matmul_f32`), so none of
890/// them dtype-fan — the importer resolves that symbol AS-IS. Binding keys:
891/// - **MatMul** `[T, T, T]` (lhs, rhs + `passthrough(lhs)` output); the batch
892/// geometry (lhs_batch_dims/rhs_batch_dims/m/n/k) rides in `OpParams::Matmul`,
893/// NOT the dtype-list. Float variants (F32/F64/BF16/F16) accumulate in
894/// f32/native and narrow on store; integer variants (I8/U8) accumulate in i32
895/// and SATURATE on store.
896/// - **FusedLinear** `[T, T, T, T]` (a, b, bias + `passthrough(a)` output);
897/// `bias` is a REQUIRED 1-D `[N]` operand (NOT `optional`, so no optional
898/// {absent, present} fan — a single 4-slot key per dtype). Reuses
899/// `OpParams::Matmul` for shape; the kernel seeds the accumulator with the
900/// bias then accumulates over `k`.
901///
902/// This map covers ONLY the portable (`kernel_source: "portable-cpu"`) kernels
903/// this contract declares. The MKL / AOCL BLAS siblings live in SEPARATE
904/// external backend crates (`fuel-mkl-cpu-backend` / `fuel-aocl-cpu-backend`),
905/// register through the exported dispatch helpers with their own
906/// `"mkl"`/`"aocl"` `kernel_source` tags as ranked ALTERNATIVES at the SAME
907/// keys, and are out of scope here (untouched). The quant `QMatMul` /
908/// `Nf4Matmul` OpKinds have their own contracts and stay hand-written. These
909/// sections have NO `##` chassis umbrella, so there is no `registrable: false`
910/// describe-only entry to omit; the SEPARATE `FusedKernelRegistry`
911/// `FusedOps::FUSED_LINEAR` seam (`register_default_fused_kernels`) is
912/// hand-written (the matmul contract declares no `fused_op`), and stays
913/// untouched — this map only serves the `KernelBindingTable` primitive path.
914pub static CPU_MATMUL_ENTRY_POINTS: &[(&str, KernelRef)] = &[
915 // Bare batched MatMul — key [T, T, T].
916 ep!("matmul", "f32", matmul_f32_cpu_wrapper),
917 ep!("matmul", "f64", matmul_f64_cpu_wrapper),
918 ep!("matmul", "bf16", matmul_bf16_cpu_wrapper),
919 ep!("matmul", "f16", matmul_f16_cpu_wrapper),
920 ep!("matmul", "i8", matmul_i8_cpu_wrapper),
921 ep!("matmul", "u8", matmul_u8_cpu_wrapper),
922 // Fused matmul + bias-add — key [T, T, T, T].
923 ep!("fused_linear", "f32", fused_linear_f32_cpu_wrapper),
924 ep!("fused_linear", "f64", fused_linear_f64_cpu_wrapper),
925 ep!("fused_linear", "bf16", fused_linear_bf16_cpu_wrapper),
926 ep!("fused_linear", "f16", fused_linear_f16_cpu_wrapper),
927];
928
929/// The CPU **attention** family's `symbol → production wrapper` map — the
930/// MIGRATED (`KernelBindingTable`) subset: forward `FlashAttn` (4 dtypes) +
931/// `FlashAttnBackward{Q,K,V}` (3 selectors × 4 dtypes) = 16 entry points.
932/// Contract: `fuel-dispatch/kernel-contracts/cpu/attention.fkc.md`.
933///
934/// Each per-(op, dtype) section (`## flash_attn_f32`, `## flash_attn_backward_q_f32`,
935/// …) declares a SPECIFIC single-dtype `entry_point`, so none of them dtype-fan
936/// — the importer resolves that symbol AS-IS. Because the contract marks
937/// `alibi_slopes` as `optional: true` (the LAST input), the importer's
938/// key-builder fans each section into BOTH the no-alibi key (`[q,k,v,out]` /
939/// `[q,k,v,do,out]`) and the with-alibi key (`+alibi`) — both resolving this
940/// SAME symbol/wrapper (the CPU wrapper handles the presence/absence of the
941/// alibi operand). The softmax/mask geometry rides in `OpParams::FlashAttn`
942/// (shared by the forward op AND the three backward selectors — there is no
943/// dedicated backward `OpParams` variant), NOT the dtype-list.
944///
945/// Two symbol shapes:
946/// - **Forward FlashAttn** kernels are byte-kernel symbols
947/// (`fuel_cpu_backend::byte_kernels::flash_attn_<dt>`, the `ep!` shape).
948/// - **FlashAttnBackward{Q,K,V}** wrappers live in the dispatch layer (they pin
949/// which gradient the shared byte-kernel writes via a `FaBackwardWhich`
950/// selector), so their symbol is the fully-qualified
951/// `fuel_dispatch::dispatch::<wrapper>` path (the `ep_dispatch!` shape).
952///
953/// **PagedAttn is REGISTRABLE** (`registrable: true` in the contract). Its
954/// `fdx.gather: paged_blocks` pool is import METADATA (§3.9.1: the block_table /
955/// context_lens ride as ordinary U32 operands + `OpParams::PagedAttn`, so
956/// registration does not depend on the FDX gather VIEW, which stays
957/// [consumer-ahead]), so the importer validates the gather block for coherence
958/// and resolves the four paged `byte_kernels::paged_attn_<dt>` symbols here. The
959/// contract marks `alibi_slopes` as `optional: true` (last input), so the
960/// key-builder fans each paged section into BOTH the no-alibi key
961/// `[q,kc,vc,bt:U32,cl:U32,out]` and the with-alibi key `+alibi` — both resolving
962/// the SAME wrapper (the CPU `paged_attn_<dt>_cpu_wrapper` handles 5 or 6 inputs).
963/// The SEPARATE `FusedKernelRegistry` FLASH_ATTN* / PAGED_ATTN seam
964/// (`register_default_fused_kernels`) is a distinct registry and stays untouched;
965/// this map only serves the `KernelBindingTable` primitive path.
966pub static CPU_ATTENTION_ENTRY_POINTS: &[(&str, KernelRef)] = &[
967 // Forward FlashAttn — byte-kernel symbols; keys [T,T,T,T] / [T,T,T,T,T].
968 ep!("flash_attn", "f32", flash_attn_f32_cpu_wrapper),
969 ep!("flash_attn", "f64", flash_attn_f64_cpu_wrapper),
970 ep!("flash_attn", "bf16", flash_attn_bf16_cpu_wrapper),
971 ep!("flash_attn", "f16", flash_attn_f16_cpu_wrapper),
972 // FlashAttnBackward{Q,K,V} — dispatch-layer wrapper symbols;
973 // keys [T,T,T,T,T] / [T,T,T,T,T,T].
974 ep_dispatch!(flash_attn_backward_q_f32_cpu_wrapper),
975 ep_dispatch!(flash_attn_backward_q_f64_cpu_wrapper),
976 ep_dispatch!(flash_attn_backward_q_bf16_cpu_wrapper),
977 ep_dispatch!(flash_attn_backward_q_f16_cpu_wrapper),
978 ep_dispatch!(flash_attn_backward_k_f32_cpu_wrapper),
979 ep_dispatch!(flash_attn_backward_k_f64_cpu_wrapper),
980 ep_dispatch!(flash_attn_backward_k_bf16_cpu_wrapper),
981 ep_dispatch!(flash_attn_backward_k_f16_cpu_wrapper),
982 ep_dispatch!(flash_attn_backward_v_f32_cpu_wrapper),
983 ep_dispatch!(flash_attn_backward_v_f64_cpu_wrapper),
984 ep_dispatch!(flash_attn_backward_v_bf16_cpu_wrapper),
985 ep_dispatch!(flash_attn_backward_v_f16_cpu_wrapper),
986 // PagedAttn — byte-kernel symbols; keys [T,T,T,U32,U32,T] (+[T] with-alibi).
987 // The optional-operand fan resolves both keys to the same per-dtype wrapper.
988 ep!("paged_attn", "f32", paged_attn_f32_cpu_wrapper),
989 ep!("paged_attn", "f64", paged_attn_f64_cpu_wrapper),
990 ep!("paged_attn", "bf16", paged_attn_bf16_cpu_wrapper),
991 ep!("paged_attn", "f16", paged_attn_f16_cpu_wrapper),
992];
993
994/// The CPU **in-place scalar-param** family's `symbol → production wrapper`
995/// map — the FULL family: 21 in-place unary ops (`<op>_inplace`, each fanned
996/// ×4 dtypes = 84) + `InplaceAffine` / `ClampInplace` / `PowIInplace`
997/// (`affine_inplace` / `clamp_inplace` / `powi_inplace`, 4 dtypes each = 12) =
998/// 96 entry points. Contract:
999/// `fuel-dispatch/kernel-contracts/cpu/inplace-unary-affine.fkc.md`.
1000///
1001/// - **In-place unary** (`relu_inplace` … `gelu_erf_inplace`): each per-op
1002/// section declares a BASE `entry_point` (`…::relu_inplace`) + enumerates
1003/// `dtypes: [F32,F64,BF16,F16]`, so the importer's §3.4 multi-dtype fan-out
1004/// resolves `<base>_<dtype>` (`relu_inplace_f32`) against this table — the
1005/// `$op` literals below are the byte-kernel BASES (`<op>_inplace`), NOT the
1006/// OpKind names -- and since S13 the two no longer share a root spelling at
1007/// all. The two GELU flavors stay distinct: base `gelu_inplace` (the real
1008/// byte_kernels symbol, unrenamed) now binds `OpKind::GeluTanhInplace` (the
1009/// canonical tanh GELU) vs base `gelu_erf_inplace` (also unrenamed) now
1010/// binding `OpKind::GeluInplace` (bare `Gelu` means erf since S13).
1011/// - **InplaceAffine / ClampInplace / PowIInplace** are per-dtype SINGLE
1012/// sections (one enumerated dtype each), so they do NOT fan — the importer
1013/// resolves their specific `<op>_inplace_<dt>` symbol AS-IS. The affine rows
1014/// carry the THREE-WAY naming skew: the byte-kernel/entry_point suffix is
1015/// `affine_inplace_<dt>` while the production wrapper fn is
1016/// `inplace_affine_<dt>_cpu_wrapper` (words swapped) — the `ep!` symbol is
1017/// built from the `$op`/`$dt` literals (`affine_inplace`), so the mapping
1018/// still binds the correct contract symbol to the swapped-name wrapper
1019/// (mirrors the clamp/powi/log_softmax fn-vs-symbol cases). Clamp/powi
1020/// wrapper fn-names match their symbols directly.
1021///
1022/// Every binding keys `[T, T]` (the single `out` operand + its
1023/// `passthrough(out)` mirror; the executor's `WorkItemKind::InplaceKernel` arm
1024/// passes the target as `outputs[0]`, so the wrapper takes 0 inputs + 1 output,
1025/// but the binding-table KEY is `[T, T]`). Scalar params (affine `mul`/`add`,
1026/// clamp `min`/`max`, powi `exp`) ride in `OpParams::{Affine, Clamp, PowI}`, NOT
1027/// the dtype-list. The `## unary_inplace` chassis umbrella is `registrable: false`
1028/// (§3.10 describe-only) and never resolves, so it is absent here.
1029pub static CPU_INPLACE_ENTRY_POINTS: &[(&str, KernelRef)] = &[
1030 // In-place unary — 21 ops × 4 dtypes, base `<op>_inplace` fanned per dtype.
1031 ep!("relu_inplace", "f32", relu_inplace_f32_cpu_wrapper),
1032 ep!("relu_inplace", "f64", relu_inplace_f64_cpu_wrapper),
1033 ep!("relu_inplace", "bf16", relu_inplace_bf16_cpu_wrapper),
1034 ep!("relu_inplace", "f16", relu_inplace_f16_cpu_wrapper),
1035 ep!("silu_inplace", "f32", silu_inplace_f32_cpu_wrapper),
1036 ep!("silu_inplace", "f64", silu_inplace_f64_cpu_wrapper),
1037 ep!("silu_inplace", "bf16", silu_inplace_bf16_cpu_wrapper),
1038 ep!("silu_inplace", "f16", silu_inplace_f16_cpu_wrapper),
1039 ep!("gelu_inplace", "f32", gelu_inplace_f32_cpu_wrapper),
1040 ep!("gelu_inplace", "f64", gelu_inplace_f64_cpu_wrapper),
1041 ep!("gelu_inplace", "bf16", gelu_inplace_bf16_cpu_wrapper),
1042 ep!("gelu_inplace", "f16", gelu_inplace_f16_cpu_wrapper),
1043 ep!("tanh_inplace", "f32", tanh_inplace_f32_cpu_wrapper),
1044 ep!("tanh_inplace", "f64", tanh_inplace_f64_cpu_wrapper),
1045 ep!("tanh_inplace", "bf16", tanh_inplace_bf16_cpu_wrapper),
1046 ep!("tanh_inplace", "f16", tanh_inplace_f16_cpu_wrapper),
1047 ep!("sigmoid_inplace", "f32", sigmoid_inplace_f32_cpu_wrapper),
1048 ep!("sigmoid_inplace", "f64", sigmoid_inplace_f64_cpu_wrapper),
1049 ep!("sigmoid_inplace", "bf16", sigmoid_inplace_bf16_cpu_wrapper),
1050 ep!("sigmoid_inplace", "f16", sigmoid_inplace_f16_cpu_wrapper),
1051 ep!("neg_inplace", "f32", neg_inplace_f32_cpu_wrapper),
1052 ep!("neg_inplace", "f64", neg_inplace_f64_cpu_wrapper),
1053 ep!("neg_inplace", "bf16", neg_inplace_bf16_cpu_wrapper),
1054 ep!("neg_inplace", "f16", neg_inplace_f16_cpu_wrapper),
1055 ep!("abs_inplace", "f32", abs_inplace_f32_cpu_wrapper),
1056 ep!("abs_inplace", "f64", abs_inplace_f64_cpu_wrapper),
1057 ep!("abs_inplace", "bf16", abs_inplace_bf16_cpu_wrapper),
1058 ep!("abs_inplace", "f16", abs_inplace_f16_cpu_wrapper),
1059 ep!("sqr_inplace", "f32", sqr_inplace_f32_cpu_wrapper),
1060 ep!("sqr_inplace", "f64", sqr_inplace_f64_cpu_wrapper),
1061 ep!("sqr_inplace", "bf16", sqr_inplace_bf16_cpu_wrapper),
1062 ep!("sqr_inplace", "f16", sqr_inplace_f16_cpu_wrapper),
1063 ep!("sqrt_inplace", "f32", sqrt_inplace_f32_cpu_wrapper),
1064 ep!("sqrt_inplace", "f64", sqrt_inplace_f64_cpu_wrapper),
1065 ep!("sqrt_inplace", "bf16", sqrt_inplace_bf16_cpu_wrapper),
1066 ep!("sqrt_inplace", "f16", sqrt_inplace_f16_cpu_wrapper),
1067 ep!("rsqrt_inplace", "f32", rsqrt_inplace_f32_cpu_wrapper),
1068 ep!("rsqrt_inplace", "f64", rsqrt_inplace_f64_cpu_wrapper),
1069 ep!("rsqrt_inplace", "bf16", rsqrt_inplace_bf16_cpu_wrapper),
1070 ep!("rsqrt_inplace", "f16", rsqrt_inplace_f16_cpu_wrapper),
1071 ep!("recip_inplace", "f32", recip_inplace_f32_cpu_wrapper),
1072 ep!("recip_inplace", "f64", recip_inplace_f64_cpu_wrapper),
1073 ep!("recip_inplace", "bf16", recip_inplace_bf16_cpu_wrapper),
1074 ep!("recip_inplace", "f16", recip_inplace_f16_cpu_wrapper),
1075 ep!("exp_inplace", "f32", exp_inplace_f32_cpu_wrapper),
1076 ep!("exp_inplace", "f64", exp_inplace_f64_cpu_wrapper),
1077 ep!("exp_inplace", "bf16", exp_inplace_bf16_cpu_wrapper),
1078 ep!("exp_inplace", "f16", exp_inplace_f16_cpu_wrapper),
1079 ep!("log_inplace", "f32", log_inplace_f32_cpu_wrapper),
1080 ep!("log_inplace", "f64", log_inplace_f64_cpu_wrapper),
1081 ep!("log_inplace", "bf16", log_inplace_bf16_cpu_wrapper),
1082 ep!("log_inplace", "f16", log_inplace_f16_cpu_wrapper),
1083 ep!("sin_inplace", "f32", sin_inplace_f32_cpu_wrapper),
1084 ep!("sin_inplace", "f64", sin_inplace_f64_cpu_wrapper),
1085 ep!("sin_inplace", "bf16", sin_inplace_bf16_cpu_wrapper),
1086 ep!("sin_inplace", "f16", sin_inplace_f16_cpu_wrapper),
1087 ep!("cos_inplace", "f32", cos_inplace_f32_cpu_wrapper),
1088 ep!("cos_inplace", "f64", cos_inplace_f64_cpu_wrapper),
1089 ep!("cos_inplace", "bf16", cos_inplace_bf16_cpu_wrapper),
1090 ep!("cos_inplace", "f16", cos_inplace_f16_cpu_wrapper),
1091 ep!("sign_inplace", "f32", sign_inplace_f32_cpu_wrapper),
1092 ep!("sign_inplace", "f64", sign_inplace_f64_cpu_wrapper),
1093 ep!("sign_inplace", "bf16", sign_inplace_bf16_cpu_wrapper),
1094 ep!("sign_inplace", "f16", sign_inplace_f16_cpu_wrapper),
1095 ep!("floor_inplace", "f32", floor_inplace_f32_cpu_wrapper),
1096 ep!("floor_inplace", "f64", floor_inplace_f64_cpu_wrapper),
1097 ep!("floor_inplace", "bf16", floor_inplace_bf16_cpu_wrapper),
1098 ep!("floor_inplace", "f16", floor_inplace_f16_cpu_wrapper),
1099 ep!("ceil_inplace", "f32", ceil_inplace_f32_cpu_wrapper),
1100 ep!("ceil_inplace", "f64", ceil_inplace_f64_cpu_wrapper),
1101 ep!("ceil_inplace", "bf16", ceil_inplace_bf16_cpu_wrapper),
1102 ep!("ceil_inplace", "f16", ceil_inplace_f16_cpu_wrapper),
1103 ep!("round_inplace", "f32", round_inplace_f32_cpu_wrapper),
1104 ep!("round_inplace", "f64", round_inplace_f64_cpu_wrapper),
1105 ep!("round_inplace", "bf16", round_inplace_bf16_cpu_wrapper),
1106 ep!("round_inplace", "f16", round_inplace_f16_cpu_wrapper),
1107 ep!("erf_inplace", "f32", erf_inplace_f32_cpu_wrapper),
1108 ep!("erf_inplace", "f64", erf_inplace_f64_cpu_wrapper),
1109 ep!("erf_inplace", "bf16", erf_inplace_bf16_cpu_wrapper),
1110 ep!("erf_inplace", "f16", erf_inplace_f16_cpu_wrapper),
1111 ep!("gelu_erf_inplace", "f32", gelu_erf_inplace_f32_cpu_wrapper),
1112 ep!("gelu_erf_inplace", "f64", gelu_erf_inplace_f64_cpu_wrapper),
1113 ep!(
1114 "gelu_erf_inplace",
1115 "bf16",
1116 gelu_erf_inplace_bf16_cpu_wrapper
1117 ),
1118 ep!("gelu_erf_inplace", "f16", gelu_erf_inplace_f16_cpu_wrapper),
1119 // InplaceAffine — symbol `affine_inplace_<dt>`, wrapper `inplace_affine_<dt>_cpu_wrapper`
1120 // (words swapped); resolved AS-IS (single-dtype sections).
1121 ep!("affine_inplace", "f32", inplace_affine_f32_cpu_wrapper),
1122 ep!("affine_inplace", "f64", inplace_affine_f64_cpu_wrapper),
1123 ep!("affine_inplace", "bf16", inplace_affine_bf16_cpu_wrapper),
1124 ep!("affine_inplace", "f16", inplace_affine_f16_cpu_wrapper),
1125 // ClampInplace — symbol == wrapper base; resolved AS-IS.
1126 ep!("clamp_inplace", "f32", clamp_inplace_f32_cpu_wrapper),
1127 ep!("clamp_inplace", "f64", clamp_inplace_f64_cpu_wrapper),
1128 ep!("clamp_inplace", "bf16", clamp_inplace_bf16_cpu_wrapper),
1129 ep!("clamp_inplace", "f16", clamp_inplace_f16_cpu_wrapper),
1130 // PowIInplace — symbol == wrapper base; resolved AS-IS.
1131 ep!("powi_inplace", "f32", powi_inplace_f32_cpu_wrapper),
1132 ep!("powi_inplace", "f64", powi_inplace_f64_cpu_wrapper),
1133 ep!("powi_inplace", "bf16", powi_inplace_bf16_cpu_wrapper),
1134 ep!("powi_inplace", "f16", powi_inplace_f16_cpu_wrapper),
1135];
1136
1137/// The CPU **cast** family's `symbol → production wrapper` map — the FULL
1138/// directed-pair matrix: every ordered pair of the 11 real numeric dtypes
1139/// {F32,F64,F16,BF16,F8E4M3,U8,I8,U32,I16,I32,I64}, identity excluded =
1140/// 11 × 10 = 110 entry points. Contract: `fuel-dispatch/kernel-contracts/cpu/cast.fkc.md`.
1141///
1142/// Each per-pair section (`## cast_f64_to_f32`, …) declares a SPECIFIC
1143/// single-dtype `src` input + a `fixed(DST)` output, so it does NOT dtype-fan —
1144/// the importer keys `[SRC, DST]` (byte-for-byte the deleted hand-written
1145/// `table.register(Cast, &[SRC, DST], …)` regs) and resolves the section's
1146/// SPECIFIC `cast_<src>_to_<dst>` byte-kernel `entry_point` AS-IS. Because the
1147/// binding lookup is keyed on the **target** dtype (the Node's dtype), all 10 of
1148/// a target's source pairs bind the SAME per-target `cast_to_<dst>_cpu_wrapper`
1149/// (which `match`es on the source dtype internally to pick the right byte
1150/// kernel) — the synthetic-umbrella precedent (10 distinct real byte-kernel
1151/// entry_points → 1 wrapper, like `pad_cpu`'s fabricated per-dtype symbols → the
1152/// one `pad_cpu_wrapper`, except here every entry_point IS a real distinct byte
1153/// kernel). This map is the SOLE registration path for the whole family: every
1154/// hand-written `table.register(Cast, …)` reg is DELETED. Identity pairs
1155/// (`[T, T]`) are never registered — the optimizer elides identity `Cast` before
1156/// dispatch. These sections have NO `##` chassis umbrella, so there is no
1157/// `registrable: false` describe-only entry to omit.
1158pub static CPU_CAST_ENTRY_POINTS: &[(&str, KernelRef)] = &[
1159 // -> F32
1160 ep!("cast", "f64_to_f32", cast_to_f32_cpu_wrapper),
1161 ep!("cast", "f16_to_f32", cast_to_f32_cpu_wrapper),
1162 ep!("cast", "bf16_to_f32", cast_to_f32_cpu_wrapper),
1163 ep!("cast", "f8e4m3_to_f32", cast_to_f32_cpu_wrapper),
1164 ep!("cast", "u8_to_f32", cast_to_f32_cpu_wrapper),
1165 ep!("cast", "i8_to_f32", cast_to_f32_cpu_wrapper),
1166 ep!("cast", "u32_to_f32", cast_to_f32_cpu_wrapper),
1167 ep!("cast", "i16_to_f32", cast_to_f32_cpu_wrapper),
1168 ep!("cast", "i32_to_f32", cast_to_f32_cpu_wrapper),
1169 ep!("cast", "i64_to_f32", cast_to_f32_cpu_wrapper),
1170 // -> F64
1171 ep!("cast", "f32_to_f64", cast_to_f64_cpu_wrapper),
1172 ep!("cast", "f16_to_f64", cast_to_f64_cpu_wrapper),
1173 ep!("cast", "bf16_to_f64", cast_to_f64_cpu_wrapper),
1174 ep!("cast", "f8e4m3_to_f64", cast_to_f64_cpu_wrapper),
1175 ep!("cast", "u8_to_f64", cast_to_f64_cpu_wrapper),
1176 ep!("cast", "i8_to_f64", cast_to_f64_cpu_wrapper),
1177 ep!("cast", "u32_to_f64", cast_to_f64_cpu_wrapper),
1178 ep!("cast", "i16_to_f64", cast_to_f64_cpu_wrapper),
1179 ep!("cast", "i32_to_f64", cast_to_f64_cpu_wrapper),
1180 ep!("cast", "i64_to_f64", cast_to_f64_cpu_wrapper),
1181 // -> F16
1182 ep!("cast", "f32_to_f16", cast_to_f16_cpu_wrapper),
1183 ep!("cast", "f64_to_f16", cast_to_f16_cpu_wrapper),
1184 ep!("cast", "bf16_to_f16", cast_to_f16_cpu_wrapper),
1185 ep!("cast", "f8e4m3_to_f16", cast_to_f16_cpu_wrapper),
1186 ep!("cast", "u8_to_f16", cast_to_f16_cpu_wrapper),
1187 ep!("cast", "i8_to_f16", cast_to_f16_cpu_wrapper),
1188 ep!("cast", "u32_to_f16", cast_to_f16_cpu_wrapper),
1189 ep!("cast", "i16_to_f16", cast_to_f16_cpu_wrapper),
1190 ep!("cast", "i32_to_f16", cast_to_f16_cpu_wrapper),
1191 ep!("cast", "i64_to_f16", cast_to_f16_cpu_wrapper),
1192 // -> BF16
1193 ep!("cast", "f32_to_bf16", cast_to_bf16_cpu_wrapper),
1194 ep!("cast", "f64_to_bf16", cast_to_bf16_cpu_wrapper),
1195 ep!("cast", "f16_to_bf16", cast_to_bf16_cpu_wrapper),
1196 ep!("cast", "f8e4m3_to_bf16", cast_to_bf16_cpu_wrapper),
1197 ep!("cast", "u8_to_bf16", cast_to_bf16_cpu_wrapper),
1198 ep!("cast", "i8_to_bf16", cast_to_bf16_cpu_wrapper),
1199 ep!("cast", "u32_to_bf16", cast_to_bf16_cpu_wrapper),
1200 ep!("cast", "i16_to_bf16", cast_to_bf16_cpu_wrapper),
1201 ep!("cast", "i32_to_bf16", cast_to_bf16_cpu_wrapper),
1202 ep!("cast", "i64_to_bf16", cast_to_bf16_cpu_wrapper),
1203 // -> F8E4M3
1204 ep!("cast", "f32_to_f8e4m3", cast_to_f8e4m3_cpu_wrapper),
1205 ep!("cast", "f64_to_f8e4m3", cast_to_f8e4m3_cpu_wrapper),
1206 ep!("cast", "f16_to_f8e4m3", cast_to_f8e4m3_cpu_wrapper),
1207 ep!("cast", "bf16_to_f8e4m3", cast_to_f8e4m3_cpu_wrapper),
1208 ep!("cast", "u8_to_f8e4m3", cast_to_f8e4m3_cpu_wrapper),
1209 ep!("cast", "i8_to_f8e4m3", cast_to_f8e4m3_cpu_wrapper),
1210 ep!("cast", "u32_to_f8e4m3", cast_to_f8e4m3_cpu_wrapper),
1211 ep!("cast", "i16_to_f8e4m3", cast_to_f8e4m3_cpu_wrapper),
1212 ep!("cast", "i32_to_f8e4m3", cast_to_f8e4m3_cpu_wrapper),
1213 ep!("cast", "i64_to_f8e4m3", cast_to_f8e4m3_cpu_wrapper),
1214 // -> U8
1215 ep!("cast", "f32_to_u8", cast_to_u8_cpu_wrapper),
1216 ep!("cast", "f64_to_u8", cast_to_u8_cpu_wrapper),
1217 ep!("cast", "f16_to_u8", cast_to_u8_cpu_wrapper),
1218 ep!("cast", "bf16_to_u8", cast_to_u8_cpu_wrapper),
1219 ep!("cast", "f8e4m3_to_u8", cast_to_u8_cpu_wrapper),
1220 ep!("cast", "i8_to_u8", cast_to_u8_cpu_wrapper),
1221 ep!("cast", "u32_to_u8", cast_to_u8_cpu_wrapper),
1222 ep!("cast", "i16_to_u8", cast_to_u8_cpu_wrapper),
1223 ep!("cast", "i32_to_u8", cast_to_u8_cpu_wrapper),
1224 ep!("cast", "i64_to_u8", cast_to_u8_cpu_wrapper),
1225 // -> I8
1226 ep!("cast", "f32_to_i8", cast_to_i8_cpu_wrapper),
1227 ep!("cast", "f64_to_i8", cast_to_i8_cpu_wrapper),
1228 ep!("cast", "f16_to_i8", cast_to_i8_cpu_wrapper),
1229 ep!("cast", "bf16_to_i8", cast_to_i8_cpu_wrapper),
1230 ep!("cast", "f8e4m3_to_i8", cast_to_i8_cpu_wrapper),
1231 ep!("cast", "u8_to_i8", cast_to_i8_cpu_wrapper),
1232 ep!("cast", "u32_to_i8", cast_to_i8_cpu_wrapper),
1233 ep!("cast", "i16_to_i8", cast_to_i8_cpu_wrapper),
1234 ep!("cast", "i32_to_i8", cast_to_i8_cpu_wrapper),
1235 ep!("cast", "i64_to_i8", cast_to_i8_cpu_wrapper),
1236 // -> U32
1237 ep!("cast", "f32_to_u32", cast_to_u32_cpu_wrapper),
1238 ep!("cast", "f64_to_u32", cast_to_u32_cpu_wrapper),
1239 ep!("cast", "f16_to_u32", cast_to_u32_cpu_wrapper),
1240 ep!("cast", "bf16_to_u32", cast_to_u32_cpu_wrapper),
1241 ep!("cast", "f8e4m3_to_u32", cast_to_u32_cpu_wrapper),
1242 ep!("cast", "u8_to_u32", cast_to_u32_cpu_wrapper),
1243 ep!("cast", "i8_to_u32", cast_to_u32_cpu_wrapper),
1244 ep!("cast", "i16_to_u32", cast_to_u32_cpu_wrapper),
1245 ep!("cast", "i32_to_u32", cast_to_u32_cpu_wrapper),
1246 ep!("cast", "i64_to_u32", cast_to_u32_cpu_wrapper),
1247 // -> I16
1248 ep!("cast", "f32_to_i16", cast_to_i16_cpu_wrapper),
1249 ep!("cast", "f64_to_i16", cast_to_i16_cpu_wrapper),
1250 ep!("cast", "f16_to_i16", cast_to_i16_cpu_wrapper),
1251 ep!("cast", "bf16_to_i16", cast_to_i16_cpu_wrapper),
1252 ep!("cast", "f8e4m3_to_i16", cast_to_i16_cpu_wrapper),
1253 ep!("cast", "u8_to_i16", cast_to_i16_cpu_wrapper),
1254 ep!("cast", "i8_to_i16", cast_to_i16_cpu_wrapper),
1255 ep!("cast", "u32_to_i16", cast_to_i16_cpu_wrapper),
1256 ep!("cast", "i32_to_i16", cast_to_i16_cpu_wrapper),
1257 ep!("cast", "i64_to_i16", cast_to_i16_cpu_wrapper),
1258 // -> I32
1259 ep!("cast", "f32_to_i32", cast_to_i32_cpu_wrapper),
1260 ep!("cast", "f64_to_i32", cast_to_i32_cpu_wrapper),
1261 ep!("cast", "f16_to_i32", cast_to_i32_cpu_wrapper),
1262 ep!("cast", "bf16_to_i32", cast_to_i32_cpu_wrapper),
1263 ep!("cast", "f8e4m3_to_i32", cast_to_i32_cpu_wrapper),
1264 ep!("cast", "u8_to_i32", cast_to_i32_cpu_wrapper),
1265 ep!("cast", "i8_to_i32", cast_to_i32_cpu_wrapper),
1266 ep!("cast", "u32_to_i32", cast_to_i32_cpu_wrapper),
1267 ep!("cast", "i16_to_i32", cast_to_i32_cpu_wrapper),
1268 ep!("cast", "i64_to_i32", cast_to_i32_cpu_wrapper),
1269 // -> I64
1270 ep!("cast", "f32_to_i64", cast_to_i64_cpu_wrapper),
1271 ep!("cast", "f64_to_i64", cast_to_i64_cpu_wrapper),
1272 ep!("cast", "f16_to_i64", cast_to_i64_cpu_wrapper),
1273 ep!("cast", "bf16_to_i64", cast_to_i64_cpu_wrapper),
1274 ep!("cast", "f8e4m3_to_i64", cast_to_i64_cpu_wrapper),
1275 ep!("cast", "u8_to_i64", cast_to_i64_cpu_wrapper),
1276 ep!("cast", "i8_to_i64", cast_to_i64_cpu_wrapper),
1277 ep!("cast", "u32_to_i64", cast_to_i64_cpu_wrapper),
1278 ep!("cast", "i16_to_i64", cast_to_i64_cpu_wrapper),
1279 ep!("cast", "i32_to_i64", cast_to_i64_cpu_wrapper),
1280 // ---- Bool cast pairs (GAP-168) ----
1281 // 11 sources -> Bool, all binding the one per-target Bool wrapper.
1282 ep!("cast", "f32_to_bool", cast_to_bool_cpu_wrapper),
1283 ep!("cast", "f64_to_bool", cast_to_bool_cpu_wrapper),
1284 ep!("cast", "f16_to_bool", cast_to_bool_cpu_wrapper),
1285 ep!("cast", "bf16_to_bool", cast_to_bool_cpu_wrapper),
1286 ep!("cast", "f8e4m3_to_bool", cast_to_bool_cpu_wrapper),
1287 ep!("cast", "u8_to_bool", cast_to_bool_cpu_wrapper),
1288 ep!("cast", "i8_to_bool", cast_to_bool_cpu_wrapper),
1289 ep!("cast", "u32_to_bool", cast_to_bool_cpu_wrapper),
1290 ep!("cast", "i16_to_bool", cast_to_bool_cpu_wrapper),
1291 ep!("cast", "i32_to_bool", cast_to_bool_cpu_wrapper),
1292 ep!("cast", "i64_to_bool", cast_to_bool_cpu_wrapper),
1293 // Bool -> 11 targets, each binding that target's existing wrapper.
1294 ep!("cast", "bool_to_f32", cast_to_f32_cpu_wrapper),
1295 ep!("cast", "bool_to_f64", cast_to_f64_cpu_wrapper),
1296 ep!("cast", "bool_to_f16", cast_to_f16_cpu_wrapper),
1297 ep!("cast", "bool_to_bf16", cast_to_bf16_cpu_wrapper),
1298 ep!("cast", "bool_to_f8e4m3", cast_to_f8e4m3_cpu_wrapper),
1299 ep!("cast", "bool_to_u8", cast_to_u8_cpu_wrapper),
1300 ep!("cast", "bool_to_i8", cast_to_i8_cpu_wrapper),
1301 ep!("cast", "bool_to_u32", cast_to_u32_cpu_wrapper),
1302 ep!("cast", "bool_to_i16", cast_to_i16_cpu_wrapper),
1303 ep!("cast", "bool_to_i32", cast_to_i32_cpu_wrapper),
1304 ep!("cast", "bool_to_i64", cast_to_i64_cpu_wrapper),
1305];
1306
1307/// The CPU norm/softmax **FUSED** family's `symbol → production wrapper` map —
1308/// the FIRST fused-registry link table (the `fused_op` analogue of the
1309/// primitive `CPU_*_ENTRY_POINTS`). Contract:
1310/// `fuel-dispatch/kernel-contracts/fused/norm-softmax.fkc.md`.
1311///
1312/// Eight `fused_op` sections — three forward (`SOFTMAX_LAST_DIM` /
1313/// `RMS_NORM_LAST_DIM` / `LAYER_NORM_LAST_DIM`, key `[T, T]`) plus five backward
1314/// helpers (`SOFTMAX_LAST_DIM_BACKWARD` / `LAYER_NORM_LAST_DIM_BACKWARD` /
1315/// `RMS_NORM_LAST_DIM_BACKWARD` / `REDUCE_MAX_TO_BACKWARD` / `POWI_BACKWARD`,
1316/// key `[T, T, T]`). Each declares a dtype-agnostic BASE `entry_point`
1317/// `…::<op>_cpu` **plus a per-input `dtypes: [F32, F64, BF16, F16]` list**, so
1318/// `crate::fkc::lower::lower_fused` **dtype-fans** it (§3.4, via the `fep!` macro) into
1319/// four per-dtype impls: per fanned `dt` it resolves `<base>_<dt>`
1320/// (`…::<op>_cpu_f32`, `…_f64`, `…_bf16`, `…_f16`) against this table (8 ops ×
1321/// 4 dtypes = 32 rows). Each row binds the `<op>_<dt>_cpu_wrapper` — the exact
1322/// kernel fn the hand-written `register_default_fused_kernels` seam registers
1323/// for that op's `dt` dtype tuple — so an imported impl binds the SAME
1324/// executable kernel per dtype (a 1:1 replacement for the hand-written per-dtype
1325/// registrations, not just the F32 representative).
1326///
1327/// This map serves the SEPARATE [`crate::fused::FusedKernelRegistry`] seam (the
1328/// join target `register_default_fused_kernels` populates), NOT the primitive
1329/// `KernelBindingTable`. It is the live resolution behind the fused import seam:
1330/// an authored `fused_op` bundle imported through [`CpuLinkRegistry`] binds the
1331/// real CPU fused kernels (FKC P9 — no raw pointers in the serialized contract).
1332pub static CPU_FUSED_NORM_ENTRY_POINTS: &[(&str, KernelRef)] = &[
1333 // Forward (key [T, T]) — SoftmaxLastDim / RmsNormLastDim / LayerNormLastDim.
1334 fep!("softmax_last_dim_cpu_f32", softmax_last_dim_f32_cpu_wrapper),
1335 fep!("softmax_last_dim_cpu_f64", softmax_last_dim_f64_cpu_wrapper),
1336 fep!(
1337 "softmax_last_dim_cpu_bf16",
1338 softmax_last_dim_bf16_cpu_wrapper
1339 ),
1340 fep!("softmax_last_dim_cpu_f16", softmax_last_dim_f16_cpu_wrapper),
1341 fep!(
1342 "rms_norm_last_dim_cpu_f32",
1343 rms_norm_last_dim_f32_cpu_wrapper
1344 ),
1345 fep!(
1346 "rms_norm_last_dim_cpu_f64",
1347 rms_norm_last_dim_f64_cpu_wrapper
1348 ),
1349 fep!(
1350 "rms_norm_last_dim_cpu_bf16",
1351 rms_norm_last_dim_bf16_cpu_wrapper
1352 ),
1353 fep!(
1354 "rms_norm_last_dim_cpu_f16",
1355 rms_norm_last_dim_f16_cpu_wrapper
1356 ),
1357 fep!(
1358 "layer_norm_last_dim_cpu_f32",
1359 layer_norm_last_dim_f32_cpu_wrapper
1360 ),
1361 fep!(
1362 "layer_norm_last_dim_cpu_f64",
1363 layer_norm_last_dim_f64_cpu_wrapper
1364 ),
1365 fep!(
1366 "layer_norm_last_dim_cpu_bf16",
1367 layer_norm_last_dim_bf16_cpu_wrapper
1368 ),
1369 fep!(
1370 "layer_norm_last_dim_cpu_f16",
1371 layer_norm_last_dim_f16_cpu_wrapper
1372 ),
1373 // Backward (key [T, T, T]) — Softmax / LayerNorm / RmsNorm backward.
1374 fep!(
1375 "softmax_last_dim_backward_cpu_f32",
1376 softmax_last_dim_backward_f32_cpu_wrapper
1377 ),
1378 fep!(
1379 "softmax_last_dim_backward_cpu_f64",
1380 softmax_last_dim_backward_f64_cpu_wrapper
1381 ),
1382 fep!(
1383 "softmax_last_dim_backward_cpu_bf16",
1384 softmax_last_dim_backward_bf16_cpu_wrapper
1385 ),
1386 fep!(
1387 "softmax_last_dim_backward_cpu_f16",
1388 softmax_last_dim_backward_f16_cpu_wrapper
1389 ),
1390 fep!(
1391 "layer_norm_last_dim_backward_cpu_f32",
1392 layer_norm_last_dim_backward_f32_cpu_wrapper
1393 ),
1394 fep!(
1395 "layer_norm_last_dim_backward_cpu_f64",
1396 layer_norm_last_dim_backward_f64_cpu_wrapper
1397 ),
1398 fep!(
1399 "layer_norm_last_dim_backward_cpu_bf16",
1400 layer_norm_last_dim_backward_bf16_cpu_wrapper
1401 ),
1402 fep!(
1403 "layer_norm_last_dim_backward_cpu_f16",
1404 layer_norm_last_dim_backward_f16_cpu_wrapper
1405 ),
1406 fep!(
1407 "rms_norm_last_dim_backward_cpu_f32",
1408 rms_norm_last_dim_backward_f32_cpu_wrapper
1409 ),
1410 fep!(
1411 "rms_norm_last_dim_backward_cpu_f64",
1412 rms_norm_last_dim_backward_f64_cpu_wrapper
1413 ),
1414 fep!(
1415 "rms_norm_last_dim_backward_cpu_bf16",
1416 rms_norm_last_dim_backward_bf16_cpu_wrapper
1417 ),
1418 fep!(
1419 "rms_norm_last_dim_backward_cpu_f16",
1420 rms_norm_last_dim_backward_f16_cpu_wrapper
1421 ),
1422 // Backward of primitives (key [T, T, T]) — ReduceMaxTo / PowI backward.
1423 fep!(
1424 "reduce_max_to_backward_cpu_f32",
1425 reduce_max_to_backward_f32_cpu_wrapper
1426 ),
1427 fep!(
1428 "reduce_max_to_backward_cpu_f64",
1429 reduce_max_to_backward_f64_cpu_wrapper
1430 ),
1431 fep!(
1432 "reduce_max_to_backward_cpu_bf16",
1433 reduce_max_to_backward_bf16_cpu_wrapper
1434 ),
1435 fep!(
1436 "reduce_max_to_backward_cpu_f16",
1437 reduce_max_to_backward_f16_cpu_wrapper
1438 ),
1439 fep!("powi_backward_cpu_f32", powi_backward_f32_cpu_wrapper),
1440 fep!("powi_backward_cpu_f64", powi_backward_f64_cpu_wrapper),
1441 fep!("powi_backward_cpu_bf16", powi_backward_bf16_cpu_wrapper),
1442 fep!("powi_backward_cpu_f16", powi_backward_f16_cpu_wrapper),
1443];
1444
1445/// The CPU **linear / quantized-matmul** FUSED family's `symbol → production
1446/// wrapper` map — the MIGRATED subset of the `audited: true` linear-quant
1447/// bundle. Contract: `fuel-dispatch/kernel-contracts/fused/linear-quant.fkc.md`.
1448///
1449/// Four of the bundle's five `fused_op` sections migrate here (the fifth,
1450/// `nf4_matmul`, is `registrable: false` — its `fdx.quant.family: AFFINE_BLOCK`
1451/// is consumer-ahead, §6 — so it never lowers/resolves and its hand-written
1452/// `FusedOps::NF4_MATMUL` regs stay authoritative; it is absent here):
1453///
1454/// - **FUSED_LINEAR** — a multi-dtype section (`a`/`b`/`bias`
1455/// `dtypes: [F32, F64, BF16, F16]`) on a dtype-agnostic BASE `entry_point`
1456/// `…::fused_linear_cpu`, so `crate::fkc::lower::lower_fused` **dtype-fans**
1457/// it (§3.4, via the `fep!` macro) into 4 per-dtype impls resolving
1458/// `…::fused_linear_cpu_<dt>`, key `[T, T, T, T]` (a, b, bias +
1459/// `passthrough(a)` out).
1460/// - **QMATMUL** — a NON-fanning section (all operands single-dtype: `a` F32,
1461/// `w_q_bytes` U32 (the LOGICAL dispatch dtype — the physical GGML block
1462/// byte-honesty rides `fdx.quant: GGML_BLOCK`, §storage logical/physical
1463/// split), output F32), so its BASE `entry_point` `…::qmatmul_cpu` resolves
1464/// AS-IS (no `_<dt>` suffix), key `[F32, U32, F32]` byte-for-byte the deleted
1465/// hand-written `QM_F32` reg.
1466/// - **INPLACE_AFFINE** — a multi-dtype section (`x`
1467/// `dtypes: [F32, F64, BF16, F16]`) fanned into 4 per-dtype impls resolving
1468/// `…::inplace_affine_cpu_<dt>`, key `[T, T]` (out aliases x). NOTE the
1469/// fn-vs-symbol skew: the fanned symbol is `inplace_affine_cpu_<dt>` (base
1470/// `…_cpu` + `_<dt>`) while the wrapper fn is `inplace_affine_<dt>_cpu_wrapper`
1471/// (`_cpu` after `_<dt>`) — spelled out per row.
1472/// - **FUSED_SOFTMAX_CROSS_ENTROPY** — a multi-dtype section (`logits`
1473/// `dtypes: [F32, F64, BF16, F16]`; `targets` fixed I64; output `fixed(F32)`)
1474/// fanned into 4 per-dtype impls resolving
1475/// `…::fused_softmax_cross_entropy_cpu_<dt>`, key `[T, I64, F32]` byte-for-byte
1476/// the deleted hand-written `FSCE_<dt>` regs (the fan varies ONLY `logits`;
1477/// `targets` I64 + `fixed(F32)` output are constant across variants).
1478///
1479/// Each fanned row binds the `<op>_<dt>_cpu_wrapper` — the exact kernel fn the
1480/// deleted hand-written `register_default_fused_kernels` seam registered for
1481/// that op's `dt` dtype tuple — so an imported impl binds the SAME executable
1482/// kernel per dtype (a 1:1 replacement, real revision hash vs the hand-written
1483/// UNTRACKED sentinel). Serves the [`crate::fused::FusedKernelRegistry`] seam
1484/// (the join target `register_default_fused_kernels` populates), NOT the
1485/// primitive `KernelBindingTable`.
1486pub static CPU_FUSED_LINEAR_QUANT_ENTRY_POINTS: &[(&str, KernelRef)] = &[
1487 // FUSED_LINEAR — key [T, T, T, T]; multi-dtype fan over {F32, F64, BF16, F16}.
1488 fep!("fused_linear_cpu_f32", fused_linear_f32_cpu_wrapper),
1489 fep!("fused_linear_cpu_f64", fused_linear_f64_cpu_wrapper),
1490 fep!("fused_linear_cpu_bf16", fused_linear_bf16_cpu_wrapper),
1491 fep!("fused_linear_cpu_f16", fused_linear_f16_cpu_wrapper),
1492 // QMATMUL — key [F32, U32, F32]; NON-fanning, BASE symbol resolved AS-IS.
1493 fep!("qmatmul_cpu", qmatmul_f32_cpu_wrapper),
1494 // INPLACE_AFFINE — key [T, T]; multi-dtype fan. Symbol `inplace_affine_cpu_<dt>`
1495 // → wrapper `inplace_affine_<dt>_cpu_wrapper` (the `_cpu`/`_<dt>` skew).
1496 fep!("inplace_affine_cpu_f32", inplace_affine_f32_cpu_wrapper),
1497 fep!("inplace_affine_cpu_f64", inplace_affine_f64_cpu_wrapper),
1498 fep!("inplace_affine_cpu_bf16", inplace_affine_bf16_cpu_wrapper),
1499 fep!("inplace_affine_cpu_f16", inplace_affine_f16_cpu_wrapper),
1500 // FUSED_SOFTMAX_CROSS_ENTROPY — key [T, I64, F32]; fan varies ONLY logits.
1501 fep!(
1502 "fused_softmax_cross_entropy_cpu_f32",
1503 fused_softmax_cross_entropy_f32_cpu_wrapper
1504 ),
1505 fep!(
1506 "fused_softmax_cross_entropy_cpu_f64",
1507 fused_softmax_cross_entropy_f64_cpu_wrapper
1508 ),
1509 fep!(
1510 "fused_softmax_cross_entropy_cpu_bf16",
1511 fused_softmax_cross_entropy_bf16_cpu_wrapper
1512 ),
1513 fep!(
1514 "fused_softmax_cross_entropy_cpu_f16",
1515 fused_softmax_cross_entropy_f16_cpu_wrapper
1516 ),
1517];
1518
1519/// The CPU **conv / RoPE / SSM** FUSED family's `symbol → production wrapper`
1520/// map — the FULL `audited: true` conv-rope bundle. Contract:
1521/// `fuel-dispatch/kernel-contracts/fused/conv-rope.fkc.md`.
1522///
1523/// All SIX `fused_op` sections migrate here; each declares a dtype-agnostic
1524/// BASE `entry_point` `…::<op>_cpu` **plus a per-input `dtypes: [F32, F64, BF16,
1525/// F16]` list**, so `crate::fkc::lower::lower_fused` **dtype-fans** it (§3.4,
1526/// via the `fep!` macro) into four per-dtype impls resolving `<base>_<dt>` — one row per
1527/// (op, dtype) below (6 ops × 4 dtypes = 24 rows). The naming skew is the norm
1528/// family's: the FANNED symbol is `<op>_cpu_<dt>` (base `…_cpu` + `_<dt>`) while
1529/// the wrapper fn is `<op>_<dt>_cpu_wrapper`.
1530///
1531/// The 24 rows fan into **32 registered impls** because two sections
1532/// multiply their key count without adding a symbol row:
1533/// - **CONV2D** / **CONV_TRANSPOSE2D** mark `bias` `optional: true`, so the
1534/// importer's key-builder fans EACH per-dtype section into BOTH the no-bias
1535/// key `[T, T, T]` and the with-bias key `[T, T, T, T]` — both resolving the
1536/// SAME fanned symbol/wrapper (`conv2d_cpu_<dt>` → `conv2d_<dt>_cpu_wrapper`;
1537/// the CPU wrapper handles 2 or 3 inputs, the optional operand riding op-params
1538/// not a distinct symbol). So conv2d = 4 rows → 8 impls, conv_transpose2d =
1539/// 4 rows → 8 impls (the CPU transposed-conv scatter kernel seeds the output
1540/// with `bias[co]` or `0`).
1541/// - **SELECTIVE_SCAN** / **SSD_CHUNK_SCAN** declare a `return.bundle`
1542/// multi-output (Option C, one packed `[y ; last_state]` buffer); the key-
1543/// builder appends the bundle's PRIMARY-slot dtype (`passthrough(u)` /
1544/// `passthrough(x)` → T) to the 5-input tail, so each keys `[T; 6]`
1545/// byte-for-byte the deleted hand-written reg. 4 rows → 4 impls each.
1546///
1547/// Net: ROPE 4 + CONV2D 8 + CONV_TRANSPOSE2D 8 + CAUSAL_CONV1D 4 +
1548/// SELECTIVE_SCAN 4 + SSD_CHUNK_SCAN 4 = 32 impls. Each row binds the exact
1549/// per-dtype kernel fn the deleted hand-written `register_default_fused_kernels`
1550/// seam registered (a 1:1 replacement, real revision hash vs the hand-written
1551/// UNTRACKED sentinel). Serves the [`crate::fused::FusedKernelRegistry`] seam
1552/// (the join target `register_default_fused_kernels` populates), NOT the
1553/// primitive `KernelBindingTable` (the `cpu/conv.fkc.md` / `cpu/ssm.fkc.md` /
1554/// `cpu/rope.fkc.md` primitive tables are separate and stay untouched).
1555pub static CPU_FUSED_CONV_ROPE_ENTRY_POINTS: &[(&str, KernelRef)] = &[
1556 // ROPE — key [T, T, T, T] (x, cos, sin + passthrough(x) out); 4 dtypes.
1557 fep!("rope_cpu_f32", rope_f32_cpu_wrapper),
1558 fep!("rope_cpu_f64", rope_f64_cpu_wrapper),
1559 fep!("rope_cpu_bf16", rope_bf16_cpu_wrapper),
1560 fep!("rope_cpu_f16", rope_f16_cpu_wrapper),
1561 // CONV2D — optional bias fans no-bias [T,T,T] + with-bias [T,T,T,T]; 4 rows → 8 impls.
1562 fep!("conv2d_cpu_f32", conv2d_f32_cpu_wrapper),
1563 fep!("conv2d_cpu_f64", conv2d_f64_cpu_wrapper),
1564 fep!("conv2d_cpu_bf16", conv2d_bf16_cpu_wrapper),
1565 fep!("conv2d_cpu_f16", conv2d_f16_cpu_wrapper),
1566 // CONV_TRANSPOSE2D — optional bias fans no-bias + with-bias; 4 rows → 8 impls.
1567 fep!("conv_transpose2d_cpu_f32", conv_transpose2d_f32_cpu_wrapper),
1568 fep!("conv_transpose2d_cpu_f64", conv_transpose2d_f64_cpu_wrapper),
1569 fep!(
1570 "conv_transpose2d_cpu_bf16",
1571 conv_transpose2d_bf16_cpu_wrapper
1572 ),
1573 fep!("conv_transpose2d_cpu_f16", conv_transpose2d_f16_cpu_wrapper),
1574 // CAUSAL_CONV1D — key [T, T, T, T] (x, weight, bias + passthrough(x) out); 4 dtypes.
1575 fep!("causal_conv1d_cpu_f32", causal_conv1d_f32_cpu_wrapper),
1576 fep!("causal_conv1d_cpu_f64", causal_conv1d_f64_cpu_wrapper),
1577 fep!("causal_conv1d_cpu_bf16", causal_conv1d_bf16_cpu_wrapper),
1578 fep!("causal_conv1d_cpu_f16", causal_conv1d_f16_cpu_wrapper),
1579 // SELECTIVE_SCAN — return.bundle: 5 inputs + primary-slot dtype → key [T; 6]; 4 dtypes.
1580 fep!("selective_scan_cpu_f32", selective_scan_f32_cpu_wrapper),
1581 fep!("selective_scan_cpu_f64", selective_scan_f64_cpu_wrapper),
1582 fep!("selective_scan_cpu_bf16", selective_scan_bf16_cpu_wrapper),
1583 fep!("selective_scan_cpu_f16", selective_scan_f16_cpu_wrapper),
1584 // SSD_CHUNK_SCAN — return.bundle: 5 inputs + primary-slot dtype → key [T; 6]; 4 dtypes.
1585 fep!("ssd_chunk_scan_cpu_f32", ssd_chunk_scan_f32_cpu_wrapper),
1586 fep!("ssd_chunk_scan_cpu_f64", ssd_chunk_scan_f64_cpu_wrapper),
1587 fep!("ssd_chunk_scan_cpu_bf16", ssd_chunk_scan_bf16_cpu_wrapper),
1588 fep!("ssd_chunk_scan_cpu_f16", ssd_chunk_scan_f16_cpu_wrapper),
1589];
1590
1591/// The built-in CPU backend's [`LinkRegistry`] — resolves a contract's
1592/// `entry_point` symbols against [`CPU_BINARY_ENTRY_POINTS`],
1593/// [`CPU_AFFINE_CLAMP_POWI_ENTRY_POINTS`], [`CPU_UNARY_ENTRY_POINTS`],
1594/// [`CPU_COMPARE_ENTRY_POINTS`], [`CPU_WHERE_ENTRY_POINTS`],
1595/// [`CPU_REDUCE_ENTRY_POINTS`], [`CPU_REDUCE_TO_ENTRY_POINTS`],
1596/// [`CPU_NORM_ENTRY_POINTS`], [`CPU_NORM_BACKWARD_ENTRY_POINTS`],
1597/// [`CPU_ROPE_ENTRY_POINTS`], [`CPU_SSM_ENTRY_POINTS`],
1598/// [`CPU_CONV_ENTRY_POINTS`], [`CPU_PADDING_ENTRY_POINTS`],
1599/// [`CPU_SHAPE_OPS_ENTRY_POINTS`], `CPU_INDEXING_ENTRY_POINTS`,
1600/// [`CPU_MATMUL_ENTRY_POINTS`],
1601/// [`CPU_ATTENTION_ENTRY_POINTS`], [`CPU_INPLACE_ENTRY_POINTS`], and
1602/// `CPU_CAST_ENTRY_POINTS`.
1603/// Unresolved → `None`, which the importer turns into a typed
1604/// `UnknownEntryPoint` error (never a panic, never a fabricated pointer).
1605pub struct CpuLinkRegistry;
1606
1607impl LinkRegistry for CpuLinkRegistry {
1608 fn resolve_primitive(&self, symbol: &str) -> Option<KernelRef> {
1609 CPU_BINARY_ENTRY_POINTS
1610 .iter()
1611 .chain(CPU_AFFINE_CLAMP_POWI_ENTRY_POINTS.iter())
1612 .chain(CPU_UNARY_ENTRY_POINTS.iter())
1613 .chain(CPU_COMPARE_ENTRY_POINTS.iter())
1614 .chain(CPU_WHERE_ENTRY_POINTS.iter())
1615 .chain(CPU_REDUCE_ENTRY_POINTS.iter())
1616 .chain(CPU_REDUCE_TO_ENTRY_POINTS.iter())
1617 .chain(CPU_NORM_ENTRY_POINTS.iter())
1618 .chain(CPU_NORM_BACKWARD_ENTRY_POINTS.iter())
1619 .chain(CPU_ROPE_ENTRY_POINTS.iter())
1620 .chain(CPU_SSM_ENTRY_POINTS.iter())
1621 .chain(CPU_CONV_ENTRY_POINTS.iter())
1622 .chain(CPU_PADDING_ENTRY_POINTS.iter())
1623 .chain(CPU_SHAPE_OPS_ENTRY_POINTS.iter())
1624 .chain(CPU_INDEXING_ENTRY_POINTS.iter())
1625 .chain(CPU_MATMUL_ENTRY_POINTS.iter())
1626 .chain(CPU_ATTENTION_ENTRY_POINTS.iter())
1627 .chain(CPU_INPLACE_ENTRY_POINTS.iter())
1628 .chain(CPU_CAST_ENTRY_POINTS.iter())
1629 .find(|(s, _)| *s == symbol)
1630 .map(|(_, k)| *k)
1631 }
1632
1633 fn resolve_fused(&self, symbol: &str) -> Option<KernelRef> {
1634 // FUSED (`fused_op`) resolution — the live fused import seam. Chains the
1635 // per-family fused entry-point tables (the norm/softmax family,
1636 // `CPU_FUSED_NORM_ENTRY_POINTS`; the linear/quant-matmul family,
1637 // `CPU_FUSED_LINEAR_QUANT_ENTRY_POINTS`; and the conv/RoPE/SSM family,
1638 // `CPU_FUSED_CONV_ROPE_ENTRY_POINTS`). Unresolved → `None`, which the
1639 // importer turns into a typed `UnknownEntryPoint` (never a panic, never a
1640 // fabricated pointer — FKC P9).
1641 //
1642 // All three checked-in fused bundles are now `audited: true` and WIRED,
1643 // PRODUCTION-migrated in `register_default_fused_kernels`:
1644 // - `fused/norm-softmax.fkc.md` — 8 sections → 32 impls (SOFTMAX /
1645 // RMS_NORM / LAYER_NORM_LAST_DIM (+backward), REDUCE_MAX_TO_BACKWARD,
1646 // POWI_BACKWARD) via `CPU_FUSED_NORM_ENTRY_POINTS`.
1647 // - `fused/linear-quant.fkc.md` — FUSED_LINEAR / QMATMUL /
1648 // INPLACE_AFFINE / FUSED_SOFTMAX_CROSS_ENTROPY (its fifth section
1649 // `nf4_matmul` is `registrable: false` — `fdx.quant.family:
1650 // AFFINE_BLOCK` consumer-ahead, §6 — so NF4's hand-written regs stay
1651 // authoritative) via `CPU_FUSED_LINEAR_QUANT_ENTRY_POINTS`.
1652 // - `fused/conv-rope.fkc.md` — all 6 sections → 32 impls (ROPE, CONV2D,
1653 // CONV_TRANSPOSE2D, CAUSAL_CONV1D, SELECTIVE_SCAN, SSD_CHUNK_SCAN) via
1654 // `CPU_FUSED_CONV_ROPE_ENTRY_POINTS`.
1655 //
1656 // Still hand-written in `register_default_fused_kernels` (no fused
1657 // contract yet): FLASH_ATTN / FLASH_ATTN_BACKWARD_{Q,K,V} / PAGED_ATTN,
1658 // plus the deferred NF4_MATMUL. The `cpu/*.fkc.md` corpora are all
1659 // primitive `op_kind` contracts (the "fused" in FusedLinear /
1660 // FusedSoftmaxCrossEntropy names an intra-op fusion, NOT a graph
1661 // `FusedOpId`); their separate primitive `KernelBindingTable` seams stay
1662 // untouched.
1663 CPU_FUSED_NORM_ENTRY_POINTS
1664 .iter()
1665 .chain(CPU_FUSED_LINEAR_QUANT_ENTRY_POINTS.iter())
1666 .chain(CPU_FUSED_CONV_ROPE_ENTRY_POINTS.iter())
1667 .find(|(s, _)| *s == symbol)
1668 .map(|(_, k)| *k)
1669 }
1670}