feat(ffai,mlx): Step-3 / DeepSeek-V3 decoder kernels — clamped SwiGLU, head-wise attn gate, sigmoid+bias router#242
Merged
Conversation
TheTom
added a commit
to TheTom/metaltile
that referenced
this pull request
May 30, 2026
…rejects mixed-dtype param sets Same fix as Step-3 PR 0xClandestine#242: CI's `cannot find spec in crate` for all three new GGUF dequant kernels traces to the legacy `bench(...)` proc-macro path emitting `crate::spec::BenchDispatch::Generic` for a `class=GenericEmpty` shape it can't pattern-match against. These dequant kernels mix concrete-dtype packed-byte inputs (`Tensor<u8>`, `Tensor<u32>`, `Tensor<f32>`) with a single generic `Tensor<T>` output; the legacy GenericEmpty path expects all-T-generic params. Switching to bare `#[kernel]` keeps the codegen / MSL-emit identical and lets the declarative `#[bench]` attribute on `kernel_benches::*` handle `tile bench` registration directly. 111/111 metaltile-std lib tests pass, workspace clippy + fmt clean.
TheTom
force-pushed
the
tom/wip/step3-kernels
branch
from
June 3, 2026 18:03
be70cda to
3abf4a9
Compare
…, head-wise attn gate, sigmoid+bias router
Three #[kernel]-DSL ports for the architectural primitives the
Step-3.5-Flash / Step-3.7-Flash decoders need that aren't already in
the metaltile-std surface. Each is a self-contained file: kernel body +
#[test_kernel] (GPU-vs-CPU-oracle) + #[bench].
- mt_clamped_swiglu (mlx/clamped_swiglu.rs):
out[i] = clip(silu(gate[i]), max=L) * clip(up[i], -L, L)
Per-layer f32 `limit` constexpr; `limit <= 0` collapses to plain
SwiGLU (shipped on an equivalence test). gpt-oss + Step-3 activation
clip pattern.
- ffai_attn_head_gate (ffai/attn_head_gate.rs):
out[h, d] = attn[h, d] * sigmoid(gate[h])
Per-head sigmoid gate applied between SDPA and o_proj. 1D elementwise;
owning head = idx / head_dim. Tested at full-attn (64h) + SWA (96h).
- ffai_moe_router_sigmoid_bias (ffai/moe_router_sigmoid_bias.rs):
scores[e] = sigmoid(logits[e]) + bias[e]
DeepSeek-V3 routing score path; the sister of moe_router_sqrtsoftplus.
Top-k selection stays in the shared pipeline. Tested at 288 + 256
experts.
All three use the proven DSL idioms (free-function `exp`, f32-suffixed
literals, `select` over ambiguous min/max, `#[constexpr]` scalars,
linear `tid`) — verified on M5 via `tile test`. Transcendental tols
match the sqrtsoftplus sibling (2e-4) for cross-GPU fast-math headroom.
TheTom
force-pushed
the
tom/wip/step3-kernels
branch
from
June 3, 2026 18:07
3abf4a9 to
1fe6c08
Compare
TheTom
enabled auto-merge (squash)
June 3, 2026 18:12
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Three
#[kernel]-DSL ports covering the architectural primitives the Step-3.5-Flash / Step-3.7-Flash decoder needs that aren't already in the metaltile-std surface. Each lands as a self-contained file: kernel body +#[test_kernel](mod kernel_tests) +#[bench](mod kernel_benches).All three are validated on M5 via
tile test— GPU output vs CPU oracle, across f32/f16/bf16.Kernels
mt_clamped_swiglumlx/clamped_swiglu.rsmt_swigluwith one-sided silu output clip + symmetricupclip — gpt-oss + Step-3 activation patternffai_attn_head_gateffai/attn_head_gate.rsout[h, d] = attn[h, d] * sigmoid(gate[h])— head-wise sigmoid attention gate, applied between SDPA ando_projffai_moe_router_sigmoid_biasffai/moe_router_sigmoid_bias.rssigmoid(logits) + bias; top-k selection stays in the shared selection pipeline1.
mt_clamped_swigluPer-layer
limitpassed as anf32constexpr.limit <= 0collapses to plain SwiGLU — validated bytest_mt_clamped_swiglu_zero_limit_equals_plain, the equivalence the per-layer dispatch ships on (most layers are limit=0; only a small subset carry non-zero limits). silu isg * sigmoid(g); clipping happens on the f32 intermediates before the cast back toT.2.
ffai_attn_head_gategateis[n_heads]— produced upstream by aLinear(hidden, n_heads). Sigmoid runs in f32 so the bf16 small-magnitude tail doesn't underflow. 1D elementwise over the flat[n_heads * head_dim]buffer; the owning head isidx / head_dim. Tests cover both full-attn (n_heads=64) and SWA (n_heads=96) shapes — the kernel doesn't bake the head count in.3.
ffai_moe_router_sigmoid_biasScore-producer kernel only. The sigmoid+bias factorisation lets each expert score be evaluated independently (no softmax denominator coupling) — the sister of
ffai_moe_router_sqrtsoftplus. Tested at bothn_experts=288andn_experts=256.Why these three
The rest of the Step-3 decoder is a recombination of existing primitives — GQA SDPA, RoPE-llama-many with partial-rotary, per-head RMSNorm, MoE top-k routing, conv2d patch embedding, 2D RoPE,
sdpa_bidirectionalfor the vision tower. The three above are the genuinely-new activation/gating/routing choices that don't fit any existing kernel.Verification
tile test(clamped_swiglu / attn_head_gate / router_sigmoid_bias) — all pass on M5, f32/f16/bf16tile test— no regressionscargo clippy --all-targets --all-features -- -D warnings— cleancargo fmt --check— clean