Skip to content

metal: drop qwen27 prompt_glue/prompt_state and core tf_lane_rope, which nothing launches (#2251) - #3065

Merged
ashhart merged 3 commits into
ashhart:mainfrom
jschmied:metal-dead-kernels-next
Nov 13, 2026
Merged

ashhart merged 3 commits into
ashhart:mainfrom
jschmied:metal-dead-kernels-next

Conversation

@jschmied

@jschmied jschmied commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

From #2251 section 4: the rest of the Metal kernels that no runtime path launches, after #2816. Head 52c867e, base main 3885dce.

What changes (2 commits, one per kernel group; 16 files, +32 −546)

  1. qwen27/prompt_glue.metal and qwen27/prompt_state.metal, as whole files: q27_prompt_product, q27_prompt_silu, q27_prompt_decay, q27_prompt_qk, q27_prompt_conv, q27_prompt_recur.
    • Caller check on main 3885dce: Qwen27 names pipelines by string literal; none of the six names appears outside its own definition.
    • Their only hosts are prompt_gpu.Kernels and prompt_state_gpu.Kernels, whose pipeline fields nothing fills (no Kernels{ literal for either). Both were kept alive only by tests/qwen27/runtime.zig:16's "force analysis" line. prompt_glue.zig and prompt_state.zig (the Conv/Recur ABIs and checks) were used only by those two wrappers and their own tests. tests/qwen27/run.zig:37 wrote the two sources out as prompt.metal, which nothing reads.
    • Removed: both .metal files, prompt_glue.zig, prompt_gpu.zig, prompt_state.zig, prompt_state_gpu.zig, their exports and test refs in qwen27.zig, the two entries in build/qwen27.zig:19 and runtime_sources/qwen27.zig:6, the prompt.metal write in run.zig:37, and the analysis line.
  2. core/shared_attention.metal's tf_lane_rope and Attention.rotate().
    • Caller check: pipeline built at src/core/shared_attention.zig:58, dispatched only from rotate() (:149-165). .rotate( on an Attention appears only in tests/lanes_see.zig:140,176,222. The two runtime users of shared_attention (gemma4/dflash.zig:112, qwen27/dflash/runtime_backend.zig:80) never call it.
    • Shape loses rotary_dim and rope (and the Rope enum, TF_RD/TF_INTERLEAVED defines): only this kernel read them. Both runtime callers drop .rotary_dim = c.head_dim. masked_attention_more.zig:108 already compiled the source without those defines.
    • lanes_see.zig feeds raw K and Q straight to append and attention; the CPU reference, masks, serial equality and prompt-unchanged checks are unchanged. Its case count halves (no split-half/interleaved loop).
  • Not deleted:
    • ops/sdpa.metal: no Zig host, but tools/zig/check_mlx_ops.py launches it against MLX (mlx_op_cases.py:222, mtp_op_cases.py:280). Left alone as asked.
    • kimi/mla.metal: live. native/kimi_metal.zig serves kimi_k3, whose layer.zig:112 sends .mla layers to mla(), which launches mla_cache, mla_qlat, mla_merge and mla_uv (layer.zig:73-78).
  • Code sharing: commit 2 is shared core (every family's Metal shared attention); 1 is Qwen27-only. No CUDA/HIP side.

One visible effect: qwen27/disk.zig's sourceHash covers qwen_runtime_sources and kernel_sources, so commits 1 and 2 both move it. Qwen27 prompt states saved to disk before this are recomputed once (as with #2816). Nothing else changes: no launched kernel's source or arguments move, so no token or speed change, and no A/B is needed.

Checks

  • Linux (GB10, aarch64): zig build and zig build test: green (686/686 steps, MUD3-ARM-GREEN).
  • x86 CI (zig build -Dfixed-cpu test test-golden test-kernel-contract test-server-security test-pull-security fuzz-cortheon check-release-paths, check-native-cuda-host compile-cuda-tests, pr_check.py, zig fmt --check, git diff --check on 8768267): green. On 52c867e, which only changes zig/runtime_sources/core.zig (built on macOS only) and CHANGELOG.md: zig fmt --check, git diff --check and pr_check.py --pr 3065 (lean 345 vs 345, 0 new) green.
  • Lean: 345 on main and branch, 0 new.
  • Mac (M3 Pro, -Dcpu=apple_m1, on 8768267, zig build test rerun on 52c867e): native build 285/285 steps. zig build test on 52c867e: 13137 of 13241 tests pass, 103 skipped, 1 fails: dashboard_test "dashboard page polls the public stats snapshot without private details", which fails the same way on main 3885dce. test-qwen27* and test-kernel-symbols-host 22/22 tests; test-lanes-see 87 cases pass (max fp32 error 1.04e-6). qwen27 kernel contract: 30 kernels pass gates 1-4 on both, and the branch's output equals main's apart from SUMMARY timings. Full contract with a fresh tf-rotary-sweep receipt (302,008,320 cases): 539 kernels pass gates 1-4, 0 fail, the 3 standing nonfinite findings, 575 of 575 controls red as planted, 0 limit problems. check_mlx_ops.py not run: the branch touches no MLX-backed kernel (ops/sdpa.metal and the MLX case files are unchanged). On 52c867e the runtime-sources modes test compiles and passes, test-metal-runtime-sources 1/1 and emit-metal-runtime-sources 7/7 steps; on 8768267 all three stop at core.zig:49:36 (no member Rope).
  • Seeded/resume/concurrency, controls, refusals: not applicable, nothing that runs changes and no new kernel, flag or refusal.
  • Overlap: 13 open PRs touch the same files, none the removed lines. One semantic clash: qwen27 Metal: DSpine drafter for Qwen3.8-27B, opt-in #2634 adds a shared_attention.Attention.init call with .rotary_dim = ...; whichever lands second drops that field (one line).

CHANGELOG: line added under Unreleased in this PR, as asked.

  • No personal data, machine names, internal hosts or local paths in the diff or its commits. No AI attribution lines.

@ashhart

ashhart commented Nov 12, 2026

Copy link
Copy Markdown
Owner

Thanks @jschmied. This is the section 4 cleanup from #2251 in the shape we agreed, and the caller check holds up.

What I checked:

  • On main 6c5cef894, the six q27_prompt_* kernels and tf_lane_rope appear only where they are defined (prompt_glue.metal, prompt_state.metal, core/shared_attention.metal:153) and in the one tf_lane_rope pipeline at src/core/shared_attention.zig:58. No pipeline is built for the other six, and .rotate( on an Attention has no runtime caller.
  • None of the 329 open PRs adds a launch of any of these kernels or a call to Attention.rotate.
  • The branch merges cleanly with today's main.

One thing blocks it. zig/runtime_sources/core.zig:48-51 still loops over attention.Rope and passes .rotary_dim = 64 and .rope = rope to attention.source, and this PR removes all three (shared_attention.zig:7-13 on the head). On macOS, zig build test builds that file. zig/build/metal_prompt_sources.zig:30 adds the runtime-source modes test to the test step, and zig/runtime_sources/modes_test.zig:32 calls root.generate, which calls core.zig's add. As far as I can read, that test, test-metal-runtime-sources and emit-metal-runtime-sources no longer compile. Linux CI and the macOS dev-tools job don't build that file, which would explain why CI is green. The fix is to drop the Rope loop and the two fields there, keeping the loop over Output.

Please also:

  1. Add the CHANGELOG line under Unreleased yourself, in the PR, including the note that saved Qwen27 prompt states are recomputed once.
  2. Expect a one-line follow-up in other open PRs that pass .rotary_dim to shared_attention.Attention.init: your own qwen27 Metal: DSpine drafter for Qwen3.8-27B, opt-in #2634 and qwen3-asr: the Metal binding (#2105, stacked on #2239) #2254, and the Qwen3.5-9B stack (Qwen3.5-9B DFlash drafter in Zig engine #1851, Qwen3.5-9B decisions in Zig engine #1871, Qwen3.5-9B grow-as-used K/V in Zig engine #1896 and Qwen3.5-9B prompt rows in one product in Zig engine #1897). Text conflicts are only with qwen27 Metal (M1-M4): projections no longer barrier internally, and XS sums compile out before tensor units (#2900) #2913 (tests/qwen27/run.zig), qwen27 metal: a prompt chunk's last layer runs q, o, the MLP and the final norm over the read row's 128-row block #2826 (qwen27.zig) and Metal: serve the dense Qwen3.8-27B straight from a GGUF file (Q4_K_M, Q8_0) (#1920) #2078 (build/qwen27.zig).

No launched kernel changes, so no token or speed A/B is needed. After the core.zig fix, please post the macOS zig build test count again, and say whether the runtime-sources compile gate ran.

  • Synthena (Ash's Agent)

@jschmied

Copy link
Copy Markdown
Contributor Author

@ashhart thanks, you were right about core.zig.

  1. zig/runtime_sources/core.zig. Fixed in 52c867e: the Rope loop and the .rotary_dim/.rope fields are gone, the loop over Output stays. On macOS (-Dcpu=apple_m1):
    • 52c867e: zig build test 836/838 steps, 13137 of 13241 tests pass, 103 skipped, 1 fails (dashboard_test, which fails the same way on main). The runtime-sources modes test ran and passes. test-metal-runtime-sources 1/1, emit-metal-runtime-sources 7/7 steps.
    • 8768267 (the old head): all three stop at core.zig:49:36: no member named 'Rope'. Our earlier Mac report counted that as a second failed step without naming it; sorry for missing it.
  2. CHANGELOG. Added under Unreleased, with the note that saved Qwen3.8-27B prompt states on Metal are recomputed once.
  3. Follow-ups in other PRs. Noted. qwen27 Metal: DSpine drafter for Qwen3.8-27B, opt-in #2634 and qwen3-asr: the Metal binding (#2105, stacked on #2239) #2254 get the one-line .rotary_dim drop when this lands.

The new commit changes only core.zig (built on macOS only) and CHANGELOG.md. zig fmt --check, git diff --check and pr_check.py --pr 3065 (lean 345 vs 345, 0 new) are green on it.

@ashhart

ashhart commented Nov 12, 2026

Copy link
Copy Markdown
Owner

Thanks @jschmied. The fix in 52c867e is what was needed. zig/runtime_sources/core.zig:49-52 now loops over Output alone, and nothing on the head still names attention.Rope, .rotary_dim on shared_attention, tf_lane_rope or the six q27_prompt_* kernels. Your macOS run covers the file Linux CI doesn't build: the modes test, test-metal-runtime-sources and emit-metal-runtime-sources all pass. The CHANGELOG line is in.

Every ask is met, and no launched kernel changes, so we'll land it.

The branch now conflicts with main in CHANGELOG.md only, at the top of Unreleased. GitHub runs no CI on a PR that conflicts, so nothing ran on this head. You don't need to rebase. We'll resolve the line when we land it and let CI run on that head first.

Thanks also for taking the .rotary_dim follow-ups in #2634 and #2254.

  • Synthena (Ash's Agent)

…x kernels nothing launches

q27_prompt_product, _silu, _decay, _qk, q27_prompt_conv and q27_prompt_recur were reached only through
prompt_gpu.Kernels and prompt_state_gpu.Kernels, which nothing builds with pipelines. The host records in
prompt_glue.zig, prompt_gpu.zig, prompt_state.zig and prompt_state_gpu.zig, the build and runtime source
lists, the runtime analysis line and the prompt.metal write in tests/qwen27/run.zig go with them.
…see calls

No family calls rotate(); the shared attention's runtime callers (Gemma 4 and Qwen27 DFlash) rope their rows
elsewhere. Shape loses rotary_dim and rope, which only the kernel read, and lanes_see feeds raw K and Q
straight to append and attention.
@ashhart
ashhart force-pushed the metal-dead-kernels-next branch from 52c867e to 77be135 Compare November 13, 2026 12:42
@ashhart
ashhart merged commit e751f29 into ashhart:main Nov 13, 2026
2 of 10 checks passed
@ashhart

ashhart commented Nov 13, 2026

Copy link
Copy Markdown
Owner

Thank you @jschmied. This is merged into main with your commits under your own name, so you're listed as a TensorFold contributor. It ships in the next release, credited to you in the changelog and the release notes.

@ashhart

ashhart commented Nov 13, 2026

Copy link
Copy Markdown
Owner

Thanks @jschmied! This is in TensorFold 1.2.8, out now: https://github.com/ashhart/TensorFold/releases/tag/v1.2.8

  • Synthena (Ash's Agent)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants