Skip to content

Commit 53a392c

Browse files
committed
fix(§METAL-RETILE): MSL-compile the sub_chunks=2 retile body (Psc1 frag-drain) + compile-check
The §METAL-RETILE sub_chunks=2 body lowered through TIR but FAILED MSL codegen: 'assigning to float4 from incompatible type simdgroup_float8x8' on the direct T.copy(dchunk_sc1, Psc1[hh, 0:headdim, 0:dstate]) — a fragment -> 3D-shared-slice copy mis-vectorizes the simdgroup matrix. Fix: drain the sc1 dchunk partial via the 2D store_f32 staging (the same vectorizable copy the main dchunk uses) then scatter store_f32 -> Psc1[hh] band with an element-wise T.Parallel copy. After the fix BOTH Metal prims MSL-compile (codegen-only, NO Apple-GPU dispatch this run per the watchdog mandate): - main_retile (sub_chunks=2, L=32,L_sub=16,P=N=32,HPC=1,threads=32): MSL 44090 B - legacy main (sub_chunks=1, HPC=2): MSL 37795 B (barrier-fix path lowers clean) scratch/compile_check_metal_retile.py is the CPU-only lower/codegen harness (guards the prod-L=64 32KB NO-GO assertion + lowers both prims). z3 driver green: ALL_POSITIVES_PROVED_AND_NON_VACUOUS (5 metal_subchunk positives UNSAT/partition + 5 paired negatives sat/miss). HONEST NO-GO: prod L=64 via this retile is 68096 B (HPC=1) / 99328 B (HPC=2), both >> Apple 32768 B — the build gate RAISES (no over-budget launch, RULE #1). The body is exercisable only at small in-budget bounded dims (compile-check). No Apple-GPU numeric measurement this run (watchdog-safety).
1 parent 58684f3 commit 53a392c

2 files changed

Lines changed: 80 additions & 1 deletion

File tree

cppmega_mlx/nn/_tilelang/mamba3_chunked_backward_core.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3744,7 +3744,15 @@ def main_retile(
37443744
transpose_A=True,
37453745
)
37463746
T.sync_threads()
3747-
T.copy(dchunk_sc1, Psc1[hh, 0:headdim, 0:dstate])
3747+
# drain the sc1 partial frag -> 2D store_f32 (vectorizable copy) ->
3748+
# Psc1[hh] band via an element-wise Parallel copy (a direct frag->3D-
3749+
# slice T.copy mis-vectorizes to float4 vs simdgroup_float8x8 in MSL).
3750+
T.copy(dchunk_sc1, store_f32[0:headdim, 0:dstate])
3751+
T.sync_threads()
3752+
for pn in T.Parallel(headdim * dstate):
3753+
pp = pn // dstate
3754+
nn = pn % dstate
3755+
Psc1[hh, pp, nn] = store_f32[pp, nn]
37483756
T.sync_threads()
37493757
T.copy(dchunk_frag, store_f32[0:headdim, 0:dstate])
37503758
T.sync_threads()
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
"""COMPILE-CHECK ONLY (no GPU dispatch) for the §METAL-RETILE sub_chunks=2 body.
2+
3+
Builds the Metal batched prim with sub_chunks=2 at a SMALL in-budget bounded shape
4+
(L=16, L_sub=8, P=N=16, HPC=1) and LOWERS it to MSL via tilelang.compile (codegen,
5+
NOT a kernel launch — safe under the watchdog mandate). Also asserts the prod-L=64
6+
build gate RAISES (honest 32 KB NO-GO) and that sub_chunks=1 stays selectable.
7+
8+
Run: .venv/bin/python scratch/compile_check_metal_retile.py
9+
"""
10+
import os
11+
os.environ.setdefault("CPPMEGA_PATH_C_METAL_GEMM_BATCHED", "1")
12+
13+
from cppmega_mlx.nn._tilelang import mamba3_chunked_backward_core as core
14+
15+
16+
def _metal_target():
17+
# Resolve the Metal target the build path uses, WITHOUT dispatching.
18+
from cppmega_mlx.nn._tilelang.mamba3_chunked_scan_core import (
19+
_resolve_chunked_compile_target,
20+
)
21+
return _resolve_chunked_compile_target(None)
22+
23+
24+
def lower_retile_small():
25+
import tilelang
26+
# bounded in-budget: L=16, sub_chunks=2 -> L_sub=8; P=N=16; HPC=1; B=1
27+
prim = core.chunk_scan_combine_bwd_metal_gemm_prim_batched(
28+
batch=1, seqlen=32, chunk_size=32, ngroups=1, nheads=1, headdim=32,
29+
dstate=32, heads_per_cta=1, sub_chunks=2, threads=32,
30+
)
31+
print("retile prim built (sub_chunks=2, L=32, L_sub=16, P=N=32, HPC=1, threads=32)")
32+
tgt = _metal_target()
33+
k = tilelang.compile(
34+
prim, out_idx=[11, 12, 13, 14, 15, 16, 17], target=tgt
35+
)
36+
src = k.get_kernel_source()
37+
print("LOWER OK: MSL length =", len(src))
38+
assert len(src) > 0
39+
return True
40+
41+
42+
def check_legacy_still_builds():
43+
import tilelang
44+
prim = core.chunk_scan_combine_bwd_metal_gemm_prim_batched(
45+
batch=1, seqlen=32, chunk_size=32, ngroups=1, nheads=2, headdim=32,
46+
dstate=32, heads_per_cta=2, sub_chunks=1, threads=32,
47+
)
48+
tgt = _metal_target()
49+
k = tilelang.compile(prim, out_idx=[11, 12, 13, 14, 15, 16, 17], target=tgt)
50+
src = k.get_kernel_source()
51+
print("LEGACY sub_chunks=1 LOWER OK (HPC=2): MSL length =", len(src))
52+
assert len(src) > 0
53+
return True
54+
55+
56+
def check_prod_l64_gate_raises():
57+
# The build-site gate must RAISE for prod L=64 (honest 32 KB NO-GO).
58+
fit1 = core._metal_subchunk_smem_bytes(32, 64, 64, 1)
59+
fit2 = core._metal_subchunk_smem_bytes(32, 64, 64, 2)
60+
print("prod L=64 fit:", "HPC=1", fit1, "HPC=2", fit2)
61+
assert fit1["all_static"] >= 32 * 1024, "HPC=1 prod unexpectedly fits 32KB"
62+
assert fit2["all_static"] >= 32 * 1024, "HPC=2 prod unexpectedly fits 32KB"
63+
print("prod L=64 retile is an honest 32KB NO-GO (gate RAISES) — confirmed")
64+
return True
65+
66+
67+
if __name__ == "__main__":
68+
check_prod_l64_gate_raises()
69+
lower_retile_small()
70+
check_legacy_still_builds()
71+
print("ALL_COMPILE_CHECKS_OK")

0 commit comments

Comments
 (0)