Skip to main content

logicaffeine_compile/codegen_cuda/
gemm_tile.rs

1//! The CUDA GEMM **tiling lattice** — the search space our optimizing compiler sweeps to climb from the
2//! naive one-thread-per-output DP4A kernel toward the cuBLAS ceiling.
3//!
4//! A [`CudaTile`] is one point: a thread block computes a `bm×bn` output tile by staging `bk`-deep slices of
5//! A and B through shared memory (so each byte is reused across the tile instead of re-read from global
6//! memory — the ~20× "tiling/reuse" lever), and each thread holds a `tm×tn` register accumulator. A point is
7//! **legal** only if it tiles the block evenly and fits the GPU's shared-memory + thread budget. The
8//! autotuner enumerates the legal lattice, measures each point on the device, and locks the fastest — the
9//! same divisor-lattice discipline as the Tenstorrent `tune_tiling`.
10
11use crate::codegen_sva::fabric_target::Precision;
12
13/// The per-block hardware budget the lattice is bounded by.
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub struct GpuLimits {
16    /// Shared memory available per block, in bytes.
17    pub smem_bytes: usize,
18    /// Threads per block.
19    pub max_threads: usize,
20}
21
22impl GpuLimits {
23    /// Ampere (sm_86 — the RTX 3060): 48 KiB shared memory per block (the portable default; up to ~99 KiB is
24    /// opt-in via `cudaFuncAttributeMaxDynamicSharedMemorySize`) and 1024 threads per block.
25    pub fn ampere() -> Self {
26        GpuLimits {
27            smem_bytes: 48 * 1024,
28            max_threads: 1024,
29        }
30    }
31}
32
33/// A point in the GEMM tiling lattice.
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub struct CudaTile {
36    /// Block (output) tile height — rows of C computed per block.
37    pub bm: usize,
38    /// Block (output) tile width — cols of C computed per block.
39    pub bn: usize,
40    /// K-stage depth — contraction tiles staged through shared memory per iteration (bigger ⇒ more reuse
41    /// per global load, up to the shared-memory budget).
42    pub bk: usize,
43    /// Per-thread register-tile height.
44    pub tm: usize,
45    /// Per-thread register-tile width.
46    pub tn: usize,
47}
48
49impl CudaTile {
50    /// Threads per block: one thread per `tm×tn` register sub-tile, so `(bm/tm)·(bn/tn)`.
51    pub fn threads(&self) -> usize {
52        if self.tm == 0 || self.tn == 0 {
53            return usize::MAX;
54        }
55        (self.bm / self.tm) * (self.bn / self.tn)
56    }
57
58    /// Shared-memory footprint in bytes: the A stage (`bm×bk`) + the B stage (`bk×bn`), int8 = 1 byte per
59    /// element, double-buffered (`×2`) so the next K-stage streams in while the current one computes.
60    pub fn smem_bytes(&self) -> usize {
61        2 * (self.bm * self.bk + self.bk * self.bn)
62    }
63
64    /// A point is legal iff it tiles the block evenly, is at least one warp, and fits the GPU's thread and
65    /// shared-memory budgets.
66    pub fn is_legal(&self, gpu: &GpuLimits) -> bool {
67        self.bm > 0
68            && self.bn > 0
69            && self.bk > 0
70            && self.tm > 0
71            && self.tn > 0
72            && self.bm % self.tm == 0
73            && self.bn % self.tn == 0
74            && self.threads() >= 32
75            && self.threads() <= gpu.max_threads
76            && self.smem_bytes() <= gpu.smem_bytes
77    }
78}
79
80/// Enumerate the legal tiling lattice for `gpu`: the divisor space of block tiles × K-stages × thread tiles.
81/// Only points that pass [`CudaTile::is_legal`] are returned, so every element is directly emittable.
82pub fn cuda_tile_lattice(gpu: &GpuLimits) -> Vec<CudaTile> {
83    let mut out = Vec::new();
84    for &bm in &[32usize, 64, 128] {
85        for &bn in &[32usize, 64, 128] {
86            for &bk in &[8usize, 16, 32] {
87                for &tm in &[2usize, 4, 8] {
88                    for &tn in &[2usize, 4, 8] {
89                        let t = CudaTile { bm, bn, bk, tm, tn };
90                        if t.is_legal(gpu) {
91                            out.push(t);
92                        }
93                    }
94                }
95            }
96        }
97    }
98    out
99}
100
101/// Emit a **shared-memory + register-blocked tiled DP4A GEMM** kernel for `tile`. Each block stages a
102/// `bm×bk` slice of A and a `bk×bn` slice of B into shared memory (every byte reused across the block —
103/// the reuse the naive one-thread-per-output kernel throws away), and each thread accumulates a `tm×tn`
104/// register tile with `__dp4a` (4 signed-int8 MACs/instruction) over the K-stage. Computes
105/// `C(M×N) = A(M×K)·B(K×N)` in int8 with int32 accumulation. The blocking constexprs are baked from `tile`,
106/// so a different lattice point yields a different kernel — genuine compiler output, not a fixed template.
107pub fn emit_tiled_dp4a(tile: &CudaTile) -> String {
108    let defs = format!(
109        "#define BM {}\n#define BN {}\n#define BK {}\n#define TM {}\n#define TN {}\n",
110        tile.bm, tile.bn, tile.bk, tile.tm, tile.tn,
111    );
112    // Header + baked #defines via format!, then a literal body that uses the macros (no brace-escaping).
113    let body = r#"#define NTHREAD ((BM / TM) * (BN / TN))
114#define COL_THREADS (BN / TN)
115
116// C(M x N) = A(M x K) . B(K x N), int8 operands, int32 accumulate, __dp4a. Block/K must divide the problem
117// (host pads); ragged M/N are bounds-guarded on the write-back.
118extern "C" __global__ void gemm_tiled_dp4a(const signed char* A, const signed char* B, int* C,
119                                           int M, int N, int K) {
120    __shared__ signed char As[BM * BK];
121    __shared__ signed char Bs[BK * BN];
122    const int block_row = blockIdx.y * BM;
123    const int block_col = blockIdx.x * BN;
124    const int tid = threadIdx.x;
125    const int thread_row = (tid / COL_THREADS) * TM;
126    const int thread_col = (tid % COL_THREADS) * TN;
127
128    int acc[TM][TN];
129    #pragma unroll
130    for (int i = 0; i < TM; ++i)
131        #pragma unroll
132        for (int j = 0; j < TN; ++j) acc[i][j] = 0;
133
134    for (int k0 = 0; k0 < K; k0 += BK) {
135        for (int idx = tid; idx < BM * BK; idx += NTHREAD) {
136            int r = idx / BK, c = idx % BK;
137            As[idx] = A[(block_row + r) * K + (k0 + c)];
138        }
139        for (int idx = tid; idx < BK * BN; idx += NTHREAD) {
140            int r = idx / BN, c = idx % BN;
141            Bs[idx] = B[(k0 + r) * N + (block_col + c)];
142        }
143        __syncthreads();
144
145        #pragma unroll
146        for (int kk = 0; kk < BK; kk += 4) {
147            #pragma unroll
148            for (int i = 0; i < TM; ++i) {
149                signed char a0 = As[(thread_row + i) * BK + kk + 0];
150                signed char a1 = As[(thread_row + i) * BK + kk + 1];
151                signed char a2 = As[(thread_row + i) * BK + kk + 2];
152                signed char a3 = As[(thread_row + i) * BK + kk + 3];
153                int aa = (a0 & 0xff) | ((a1 & 0xff) << 8) | ((a2 & 0xff) << 16) | ((a3 & 0xff) << 24);
154                #pragma unroll
155                for (int j = 0; j < TN; ++j) {
156                    signed char b0 = Bs[(kk + 0) * BN + thread_col + j];
157                    signed char b1 = Bs[(kk + 1) * BN + thread_col + j];
158                    signed char b2 = Bs[(kk + 2) * BN + thread_col + j];
159                    signed char b3 = Bs[(kk + 3) * BN + thread_col + j];
160                    int bb = (b0 & 0xff) | ((b1 & 0xff) << 8) | ((b2 & 0xff) << 16) | ((b3 & 0xff) << 24);
161                    acc[i][j] = __dp4a(aa, bb, acc[i][j]);
162                }
163            }
164        }
165        __syncthreads();
166    }
167
168    #pragma unroll
169    for (int i = 0; i < TM; ++i)
170        #pragma unroll
171        for (int j = 0; j < TN; ++j) {
172            int gr = block_row + thread_row + i;
173            int gc = block_col + thread_col + j;
174            if (gr < M && gc < N) C[gr * N + gc] = acc[i][j];
175        }
176}
177"#;
178    format!(
179        "// Auto-generated by LOGOS codegen_cuda::gemm_tile — tiled DP4A GEMM (bm={} bn={} bk={} tm={} tn={}).\n\
180         #include <cstdint>\n{defs}{body}",
181        tile.bm, tile.bn, tile.bk, tile.tm, tile.tn,
182    )
183}
184
185/// Emit a COMPLETE self-contained tiled-DP4A program for shape `M×K×N`: the tiled kernel plus a host `main`
186/// that fills A/B deterministically, launches `gemm_tiled_dp4a<<<(N/BN, M/BM), NTHREAD>>>`, and prints the
187/// int32 result row-major (one value per line) — so its output is directly comparable to a scalar oracle.
188/// Requires the shape to be tile-aligned (`M%bm==0`, `N%bn==0`, `K%bk==0`).
189pub fn emit_tiled_dp4a_program(tile: &CudaTile, m: usize, k: usize, n: usize) -> String {
190    let kernel = emit_tiled_dp4a(tile);
191    let dims = format!("#define MM {m}\n#define KK {k}\n#define NN {n}\n");
192    let host = r#"#include <cstdio>
193#include <cstdlib>
194int main() {
195    const int M = MM, K = KK, N = NN;
196    signed char* hA = (signed char*)malloc((size_t)M * K);
197    signed char* hB = (signed char*)malloc((size_t)K * N);
198    for (long i = 0; i < (long)M * K; i++) hA[i] = (signed char)(((i * 7 + 3) % 251) - 125);
199    for (long i = 0; i < (long)K * N; i++) hB[i] = (signed char)(((i * 5 + 1) % 251) - 125);
200    signed char *dA, *dB; int* dC;
201    cudaMalloc((void**)&dA, (size_t)M * K);
202    cudaMalloc((void**)&dB, (size_t)K * N);
203    cudaMalloc((void**)&dC, (size_t)M * N * sizeof(int));
204    cudaMemcpy(dA, hA, (size_t)M * K, cudaMemcpyHostToDevice);
205    cudaMemcpy(dB, hB, (size_t)K * N, cudaMemcpyHostToDevice);
206    dim3 grid(N / BN, M / BM);
207    dim3 block(NTHREAD);
208    gemm_tiled_dp4a<<<grid, block>>>(dA, dB, dC, M, N, K);
209    cudaDeviceSynchronize();
210    int* hC = (int*)malloc((size_t)M * N * sizeof(int));
211    cudaMemcpy(hC, dC, (size_t)M * N * sizeof(int), cudaMemcpyDeviceToHost);
212    for (long i = 0; i < (long)M * N; i++) printf("%d\n", hC[i]);
213    return 0;
214}
215"#;
216    format!("{kernel}{dims}{host}")
217}
218
219/// Emit a TIMING bench for the tiled kernel at `M×K×N`: fills A/B on device, warms up, times 30 launches
220/// with CUDA events, and prints `GOPS <value>` (2·M·N·K / seconds). Used by the autotuner to measure each
221/// lattice point on the real device and lock the fastest.
222pub fn emit_tiled_dp4a_bench(tile: &CudaTile, m: usize, k: usize, n: usize) -> String {
223    let kernel = emit_tiled_dp4a(tile);
224    let dims = format!("#define MM {m}\n#define KK {k}\n#define NN {n}\n");
225    let host = r#"#include <cstdio>
226#include <cstdlib>
227int main() {
228    const long M = MM, K = KK, N = NN;
229    signed char *dA, *dB; int* dC;
230    cudaMalloc((void**)&dA, (size_t)M * K);
231    cudaMalloc((void**)&dB, (size_t)K * N);
232    cudaMalloc((void**)&dC, (size_t)M * N * sizeof(int));
233    cudaMemset(dA, 1, (size_t)M * K);
234    cudaMemset(dB, 1, (size_t)K * N);
235    dim3 grid(N / BN, M / BM);
236    dim3 block(NTHREAD);
237    for (int w = 0; w < 5; w++) gemm_tiled_dp4a<<<grid, block>>>(dA, dB, dC, M, N, K);
238    cudaDeviceSynchronize();
239    cudaEvent_t s, e; cudaEventCreate(&s); cudaEventCreate(&e);
240    const int ITERS = 30; float total = 0;
241    for (int it = 0; it < ITERS; it++) {
242        cudaEventRecord(s); gemm_tiled_dp4a<<<grid, block>>>(dA, dB, dC, M, N, K); cudaEventRecord(e); cudaEventSynchronize(e);
243        float ms = 0; cudaEventElapsedTime(&ms, s, e); total += ms;
244    }
245    double gops = 2.0 * (double)M * (double)N * (double)K / ((total/ITERS) / 1000.0) / 1e9;
246    printf("ms %.4f\nGOPS %.1f\n", total/ITERS, gops);
247    return 0;
248}
249"#;
250    format!("{kernel}{dims}{host}")
251}
252
253// ───────────────────────────── WMMA int8 tensor cores (the ~18× lever to cuBLAS) ─────────────────────────
254// cuBLAS int8 runs on Ampere's IMMA tensor cores; our DP4A runs on the general CUDA cores. The only way to
255// close the remaining gap is to emit the SAME tensor-core instructions — `nvcuda::wmma` int8 fragments
256// (16×16×16 M×N×K). The fragment shape fixes the tile granularity, so a WMMA point is only legal on a
257// 16-aligned shape (`wmma_legal`). This is the compute-primitive axis of the unified lattice; the autotuner
258// picks WMMA when measured strictly better (big GEMM) and DP4A otherwise (GEMV / tiny / non-16 / overflow).
259
260/// A shape is WMMA-legal iff every dimension is a multiple of the int8 fragment size (16).
261pub fn wmma_legal(m: usize, k: usize, n: usize) -> bool {
262    m % 16 == 0 && k % 16 == 0 && n % 16 == 0
263}
264
265/// Emit a WMMA int8 tensor-core GEMM kernel: one warp per 16×16 output tile, accumulating over K in
266/// 16-wide fragments with `wmma::mma_sync` (the IMMA path). Row-major A(M×K)·B(K×N) → int32 C(M×N).
267pub fn emit_wmma_i8() -> String {
268    r#"// Auto-generated by LOGOS codegen_cuda::gemm_tile — WMMA int8 tensor-core GEMM (IMMA).
269#include <cstdint>
270#include <mma.h>
271using namespace nvcuda;
272
273#define WM 16
274#define WN 16
275#define WK 16
276
277// C(M x N) = A(M x K) . B(K x N), int8 operands, int32 accumulate, on the tensor cores. One warp computes a
278// 16x16 output tile; M, N, K must be multiples of 16 (wmma_legal).
279extern "C" __global__ void gemm_wmma_i8(const signed char* A, const signed char* B, int* C,
280                                        int M, int N, int K) {
281    const int tile_row = blockIdx.y; // which 16-row block of C this warp owns
282    const int tile_col = blockIdx.x; // which 16-col block of C this warp owns
283
284    wmma::fragment<wmma::matrix_a, WM, WN, WK, signed char, wmma::row_major> a_frag;
285    wmma::fragment<wmma::matrix_b, WM, WN, WK, signed char, wmma::row_major> b_frag;
286    wmma::fragment<wmma::accumulator, WM, WN, WK, int> acc_frag;
287    wmma::fill_fragment(acc_frag, 0);
288
289    for (int k = 0; k < K; k += WK) {
290        wmma::load_matrix_sync(a_frag, A + tile_row * WM * K + k, K);       // A[tile_row*16 .. , k ..], ld=K
291        wmma::load_matrix_sync(b_frag, B + k * N + tile_col * WN, N);       // B[k .. , tile_col*16 ..], ld=N
292        wmma::mma_sync(acc_frag, a_frag, b_frag, acc_frag);
293    }
294    wmma::store_matrix_sync(C + tile_row * WM * N + tile_col * WN, acc_frag, N, wmma::mem_row_major);
295}
296"#
297    .to_string()
298}
299
300/// Full self-contained WMMA int8 program for `M×K×N`: kernel + host `main` that fills A/B deterministically
301/// (same fill as the tiled program, so the SAME scalar oracle applies), launches
302/// `gemm_wmma_i8<<<(N/16, M/16), 32>>>` (one warp per 16×16 tile), and prints int32 C row-major.
303pub fn emit_wmma_i8_program(m: usize, k: usize, n: usize) -> String {
304    let kernel = emit_wmma_i8();
305    let dims = format!("#define MM {m}\n#define KK {k}\n#define NN {n}\n");
306    let host = r#"#include <cstdio>
307#include <cstdlib>
308int main() {
309    const int M = MM, K = KK, N = NN;
310    signed char* hA = (signed char*)malloc((size_t)M * K);
311    signed char* hB = (signed char*)malloc((size_t)K * N);
312    for (long i = 0; i < (long)M * K; i++) hA[i] = (signed char)(((i * 7 + 3) % 251) - 125);
313    for (long i = 0; i < (long)K * N; i++) hB[i] = (signed char)(((i * 5 + 1) % 251) - 125);
314    signed char *dA, *dB; int* dC;
315    cudaMalloc((void**)&dA, (size_t)M * K);
316    cudaMalloc((void**)&dB, (size_t)K * N);
317    cudaMalloc((void**)&dC, (size_t)M * N * sizeof(int));
318    cudaMemcpy(dA, hA, (size_t)M * K, cudaMemcpyHostToDevice);
319    cudaMemcpy(dB, hB, (size_t)K * N, cudaMemcpyHostToDevice);
320    dim3 grid(N / 16, M / 16);
321    gemm_wmma_i8<<<grid, 32>>>(dA, dB, dC, M, N, K);
322    cudaDeviceSynchronize();
323    int* hC = (int*)malloc((size_t)M * N * sizeof(int));
324    cudaMemcpy(hC, dC, (size_t)M * N * sizeof(int), cudaMemcpyDeviceToHost);
325    for (long i = 0; i < (long)M * N; i++) printf("%d\n", hC[i]);
326    return 0;
327}
328"#;
329    format!("{kernel}{dims}{host}")
330}
331
332/// Timing bench for the WMMA int8 kernel: warms up, times 30 launches, prints `GOPS <value>`.
333pub fn emit_wmma_i8_bench(m: usize, k: usize, n: usize) -> String {
334    let kernel = emit_wmma_i8();
335    let dims = format!("#define MM {m}\n#define KK {k}\n#define NN {n}\n");
336    let host = r#"#include <cstdio>
337#include <cstdlib>
338int main() {
339    const long M = MM, K = KK, N = NN;
340    signed char *dA, *dB; int* dC;
341    cudaMalloc((void**)&dA, (size_t)M * K);
342    cudaMalloc((void**)&dB, (size_t)K * N);
343    cudaMalloc((void**)&dC, (size_t)M * N * sizeof(int));
344    cudaMemset(dA, 1, (size_t)M * K);
345    cudaMemset(dB, 1, (size_t)K * N);
346    dim3 grid(N / 16, M / 16);
347    for (int w = 0; w < 5; w++) gemm_wmma_i8<<<grid, 32>>>(dA, dB, dC, M, N, K);
348    cudaDeviceSynchronize();
349    cudaEvent_t s, e; cudaEventCreate(&s); cudaEventCreate(&e);
350    const int ITERS = 30; float total = 0;
351    for (int it = 0; it < ITERS; it++) {
352        cudaEventRecord(s); gemm_wmma_i8<<<grid, 32>>>(dA, dB, dC, M, N, K); cudaEventRecord(e); cudaEventSynchronize(e);
353        float ms = 0; cudaEventElapsedTime(&ms, s, e); total += ms;
354    }
355    double gops = 2.0 * (double)M * (double)N * (double)K / ((total/ITERS) / 1000.0) / 1e9;
356    printf("ms %.4f\nGOPS %.1f\n", total/ITERS, gops);
357    return 0;
358}
359"#;
360    format!("{kernel}{dims}{host}")
361}
362
363// ─────────────────────── HMMA bf16/f16 tensor cores — the half-precision GEMM path ───────────────────────
364// Blackwell/Hopper/Ampere run bf16/f16 on the SAME 16×16×16 WMMA fragments as int8 IMMA, differing only in the
365// operand type (`__nv_bfloat16`/`__half`) and an f32 accumulator. So the whole int8 tile machinery (wmma_legal,
366// one-warp-per-16×16-tile, the K-loop) carries over; `MmaKind` selects the instruction from the requested
367// precision + shape + arch, and `emit_wmma` emits the fragment loop over the right types.
368
369/// The tensor-core (or CUDA-core) MMA family a GEMM lowers to, keyed on operand PRECISION. Int8 (`Dp4aI8`
370/// CUDA-core, `WmmaI8` IMMA) already exist; `HmmaBf16`/`HmmaF16` add the half-precision tensor-core path.
371/// (FP8/NVFP4 join later via the `mma.sync`/`tcgen05` PTX path on Hopper/Blackwell.)
372#[derive(Debug, Clone, Copy, PartialEq, Eq)]
373pub enum MmaKind {
374    /// `__dp4a` on the CUDA cores — the universal int8 fallback (any shape, sm_61+).
375    Dp4aI8,
376    /// IMMA int8 tensor cores, 16×16×16 s8→s32 (sm_75+, 16-aligned).
377    WmmaI8,
378    /// HMMA bf16 tensor cores, 16×16×16 bf16→f32 (sm_80+, 16-aligned).
379    HmmaBf16,
380    /// HMMA fp16 tensor cores, 16×16×16 f16→f32 (sm_70+, 16-aligned).
381    HmmaF16,
382}
383
384impl MmaKind {
385    /// The CUDA operand element type.
386    pub fn operand_ctype(self) -> &'static str {
387        match self {
388            MmaKind::HmmaBf16 => "__nv_bfloat16",
389            MmaKind::HmmaF16 => "__half",
390            _ => "signed char",
391        }
392    }
393    /// The accumulator / output element type — `float` for the half paths, `int` for int8.
394    pub fn acc_ctype(self) -> &'static str {
395        match self {
396            MmaKind::HmmaBf16 | MmaKind::HmmaF16 => "float",
397            _ => "int",
398        }
399    }
400    /// The extra header the operand type needs.
401    pub fn header(self) -> &'static str {
402        match self {
403            MmaKind::HmmaBf16 => "#include <cuda_bf16.h>\n",
404            MmaKind::HmmaF16 => "#include <cuda_fp16.h>\n",
405            _ => "",
406        }
407    }
408    /// Short tag for kernel naming / provenance.
409    pub fn tag(self) -> &'static str {
410        match self {
411            MmaKind::Dp4aI8 => "dp4a_i8",
412            MmaKind::WmmaI8 => "i8",
413            MmaKind::HmmaBf16 => "bf16",
414            MmaKind::HmmaF16 => "f16",
415        }
416    }
417    /// The minimum SM architecture the instruction needs.
418    pub fn min_arch(self) -> u32 {
419        match self {
420            MmaKind::Dp4aI8 => 61,
421            MmaKind::HmmaF16 => 70,
422            MmaKind::WmmaI8 => 75,
423            MmaKind::HmmaBf16 => 80,
424        }
425    }
426    /// Whether this runs on the tensor cores (WMMA) vs the CUDA cores (DP4A).
427    pub fn is_tensor_core(self) -> bool {
428        !matches!(self, MmaKind::Dp4aI8)
429    }
430    /// Bytes per operand element — 2 for bf16/f16, 1 for int8. Sizes shared-memory footprints and the
431    /// cp.async elements-per-16-byte-chunk (`EPC = 16 / operand_bytes`).
432    pub fn operand_bytes(self) -> usize {
433        match self {
434            MmaKind::HmmaBf16 | MmaKind::HmmaF16 => 2,
435            MmaKind::Dp4aI8 | MmaKind::WmmaI8 => 1,
436        }
437    }
438    /// Host-side conversion of a float `expr` into this kind's operand type.
439    pub fn host_convert(self, expr: &str) -> String {
440        match self {
441            MmaKind::HmmaBf16 => format!("__float2bfloat16((float)({expr}))"),
442            MmaKind::HmmaF16 => format!("__float2half((float)({expr}))"),
443            _ => format!("(signed char)({expr})"),
444        }
445    }
446}
447
448/// Select the MMA kind for a `(precision, shape, sm_arch)`. Tensor-core WMMA when the shape is 16-aligned and
449/// the arch provides the precision's HMMA/IMMA; otherwise the always-legal DP4A CUDA-core fallback — we never
450/// emit a tensor-core kernel on a shape or GPU that cannot run it.
451pub fn select_mma(prec: Precision, m: usize, k: usize, n: usize, sm_arch: u32) -> MmaKind {
452    let aligned = wmma_legal(m, k, n);
453    match prec {
454        Precision::Bf16 if aligned && sm_arch >= 80 => MmaKind::HmmaBf16,
455        Precision::Fp16 if aligned && sm_arch >= 70 => MmaKind::HmmaF16,
456        Precision::Int8 if aligned && sm_arch >= 75 => MmaKind::WmmaI8,
457        _ => MmaKind::Dp4aI8,
458    }
459}
460
461/// Which half-precision WMMA GEMM kernel is the FASTEST legal choice for a shape.
462#[derive(Debug, Clone, Copy, PartialEq, Eq)]
463pub enum HmmaKernel {
464    /// cp.async double-buffered — the measured 1.43× lever; needs sm_80+, the tile to divide the shape evenly,
465    /// and the (operand-byte-sized) double-buffered footprint to fit static shared memory.
466    CpAsync { tile: WmmaTile, stages: usize },
467    /// The basic one-warp-per-16×16-tile kernel — any 16-aligned shape (the fallback when cp.async is illegal).
468    Basic,
469}
470
471/// Select the fastest LEGAL half-precision kernel for `(kind, shape, arch)`: cp.async when the default tile
472/// divides the shape and its bf16/f16 footprint fits static shared memory on an Ampere+ part, else the basic
473/// kernel. Callers pass a shape already known WMMA-legal (16-aligned) via [`select_mma`].
474pub fn select_hmma_kernel(
475    kind: MmaKind,
476    m: usize,
477    k: usize,
478    n: usize,
479    sm_arch: u32,
480    gpu: &GpuLimits,
481) -> HmmaKernel {
482    let tile = default_wmma_tile();
483    let stages = 2;
484    let epc = 16 / kind.operand_bytes(); // elements per 16-byte cp.async chunk
485    let tile_divides = m % tile.bm == 0 && n % tile.bn == 0 && k % tile.bk == 0;
486    let chunks_align = tile.bk % epc == 0 && tile.bn % epc == 0;
487    // Double-buffered footprint in BYTES (cp.async's own predicate is element-sized / int8-only).
488    let footprint = stages * (tile.bm * tile.bk + tile.bk * tile.bn) * kind.operand_bytes();
489    if sm_arch >= 80
490        && tile_divides
491        && chunks_align
492        && footprint <= gpu.smem_bytes
493        && tile.threads() <= gpu.max_threads
494    {
495        HmmaKernel::CpAsync { tile, stages }
496    } else {
497        HmmaKernel::Basic
498    }
499}
500
501/// The knob lattice's own best bf16 rung for a shape, as a runnable benchmark: the kernel
502/// [`select_hmma_kernel`] picks (cp.async when its tile divides the shape and fits, else the basic WMMA
503/// kernel). This is the backend-native baseline a lowering measures itself against — the fair opponent is
504/// the best thing this lattice can already emit, not its worst.
505pub fn lattice_best_bf16_bench(
506    m: usize,
507    k: usize,
508    n: usize,
509    sm_arch: u32,
510    gpu: &GpuLimits,
511) -> String {
512    match select_hmma_kernel(MmaKind::HmmaBf16, m, k, n, sm_arch, gpu) {
513        HmmaKernel::CpAsync { tile, stages } => {
514            emit_wmma_cpasync_bench(MmaKind::HmmaBf16, &tile, stages, m, k, n)
515        }
516        HmmaKernel::Basic => emit_wmma_basic_bench(MmaKind::HmmaBf16, m, k, n),
517    }
518}
519
520/// Emit a WMMA tensor-core GEMM kernel for a tensor-core [`MmaKind`] — the SAME 16×16×16 fragment loop as the
521/// int8 IMMA path, over the operand/accumulator types the precision fixes (bf16/f16 → f32 accumulate). Row-major
522/// `A(M×K)·B(K×N)`. (Not for `Dp4aI8`, which is a CUDA-core kernel; use [`emit_tiled_dp4a`].)
523pub fn emit_wmma(kind: MmaKind) -> String {
524    let op = kind.operand_ctype();
525    let acc = kind.acc_ctype();
526    let tag = kind.tag();
527    let header = kind.header();
528    let zero = if acc == "float" { "0.0f" } else { "0" };
529    format!(
530        r#"// Auto-generated by LOGOS codegen_cuda::gemm_tile — WMMA {tag} tensor-core GEMM ({op} -> {acc}).
531#include <cstdint>
532#include <mma.h>
533{header}using namespace nvcuda;
534
535#define WM 16
536#define WN 16
537#define WK 16
538
539// C(M x N) = A(M x K) . B(K x N); {op} operands, {acc} accumulate, on the tensor cores. One warp = a 16x16
540// output tile; M, N, K multiples of 16 (wmma_legal).
541extern "C" __global__ void gemm_wmma_{tag}(const {op}* A, const {op}* B, {acc}* C, int M, int N, int K) {{
542    const int tile_row = blockIdx.y;
543    const int tile_col = blockIdx.x;
544    wmma::fragment<wmma::matrix_a, WM, WN, WK, {op}, wmma::row_major> a_frag;
545    wmma::fragment<wmma::matrix_b, WM, WN, WK, {op}, wmma::row_major> b_frag;
546    wmma::fragment<wmma::accumulator, WM, WN, WK, {acc}> acc_frag;
547    wmma::fill_fragment(acc_frag, {zero});
548    for (int k = 0; k < K; k += WK) {{
549        wmma::load_matrix_sync(a_frag, A + tile_row * WM * K + k, K);
550        wmma::load_matrix_sync(b_frag, B + k * N + tile_col * WN, N);
551        wmma::mma_sync(acc_frag, a_frag, b_frag, acc_frag);
552    }}
553    wmma::store_matrix_sync(C + tile_row * WM * N + tile_col * WN, acc_frag, N, wmma::mem_row_major);
554}}
555"#,
556        tag = tag,
557        op = op,
558        acc = acc,
559        header = header,
560        zero = zero,
561    )
562}
563
564/// Full self-contained WMMA program for `M×K×N` at [`MmaKind`] `kind` — fills A/B with small integers (exact in
565/// bf16/f16/int8), runs one warp per 16×16 tile, and prints `Σ C` (a double). Because the operands are exact
566/// small integers and the accumulate is wide (f32/int32), `Σ C` is an exact integer, so a scalar integer GEMM
567/// is the ≡ oracle regardless of precision.
568pub fn emit_wmma_program(kind: MmaKind, m: usize, k: usize, n: usize) -> String {
569    let kernel = emit_wmma(kind);
570    let op = kind.operand_ctype();
571    let acc = kind.acc_ctype();
572    let tag = kind.tag();
573    let conv_a = kind.host_convert("(i % 7) - 3");
574    let conv_b = kind.host_convert("(i % 5) - 2");
575    format!(
576        r#"{kernel}#include <cstdio>
577#include <cstdlib>
578#define MM {m}
579#define KK {k}
580#define NN {n}
581int main() {{
582    const int M = MM, K = KK, N = NN;
583    {op}* hA = ({op}*)malloc((size_t)M * K * sizeof({op}));
584    {op}* hB = ({op}*)malloc((size_t)K * N * sizeof({op}));
585    for (long i = 0; i < (long)M * K; i++) hA[i] = {conv_a};
586    for (long i = 0; i < (long)K * N; i++) hB[i] = {conv_b};
587    {op} *dA, *dB; {acc}* dC;
588    cudaMalloc((void**)&dA, (size_t)M * K * sizeof({op}));
589    cudaMalloc((void**)&dB, (size_t)K * N * sizeof({op}));
590    cudaMalloc((void**)&dC, (size_t)M * N * sizeof({acc}));
591    cudaMemcpy(dA, hA, (size_t)M * K * sizeof({op}), cudaMemcpyHostToDevice);
592    cudaMemcpy(dB, hB, (size_t)K * N * sizeof({op}), cudaMemcpyHostToDevice);
593    dim3 grid(N / 16, M / 16);
594    gemm_wmma_{tag}<<<grid, 32>>>(dA, dB, dC, M, N, K);
595    cudaDeviceSynchronize();
596    {acc}* hC = ({acc}*)malloc((size_t)M * N * sizeof({acc}));
597    cudaMemcpy(hC, dC, (size_t)M * N * sizeof({acc}), cudaMemcpyDeviceToHost);
598    double sum = 0.0; for (long i = 0; i < (long)M * N; i++) sum += (double)hC[i];
599    printf("%.1f\n", sum);
600    return 0;
601}}
602"#,
603        kernel = kernel,
604        op = op,
605        acc = acc,
606        tag = tag,
607        m = m,
608        k = k,
609        n = n,
610        conv_a = conv_a,
611        conv_b = conv_b,
612    )
613}
614
615// ─────────────────────── correct-by-construction tile selection (roofline cost model) ───────────────────────
616// "Mathematically optimally tile" = CONSTRUCT the tile that minimizes an analytical cost model over the
617// provably-complete FEASIBLE region, rather than sweep-and-hope. The primary term is closed-form and
618// data-confirmed: global memory traffic ∝ (1/BM + 1/BN) (each A-element is read N/BN times, each B-element
619// M/BM times), so a bigger block tile strictly reduces traffic — until it stops fitting registers/shared
620// memory. The construction returns the min-traffic FEASIBLE tile; the GPU then verifies the last mile (the
621// second-order occupancy/pipeline effects the roofline does not model). Correct by construction: legal (fits
622// the hardware) + cost-optimal (min over the feasible region) + semantically the GEMM (the emitter + oracle).
623
624/// SM-level register file on Ampere (sm_86): 65,536 32-bit registers per SM. A tile needs
625/// `regs_per_thread · threads ≤ this` to place at least one block per SM.
626pub const AMPERE_SM_REGS: usize = 65536;
627
628impl CudaTile {
629    /// Registers per thread ≈ the `tm·tn` int32 accumulator tile plus ~24 for indices/fragments/loop state.
630    pub fn regs_per_thread(&self) -> usize {
631        self.tm * self.tn + 24
632    }
633
634    /// Feasible-for-construction: legal on the block budget AND at least one block fits the SM register file.
635    pub fn is_feasible(&self, gpu: &GpuLimits) -> bool {
636        self.is_legal(gpu) && self.regs_per_thread() * self.threads() <= AMPERE_SM_REGS
637    }
638
639    /// Estimated resident blocks per SM (Ampere sm_86: 100 KiB smem, 65,536 regs, 1536 threads, ≤16 blocks) —
640    /// the min over the four limiters. Bigger tiles (more regs/smem) → fewer blocks → less latency hiding.
641    pub fn blocks_per_sm(&self) -> usize {
642        let by_regs = AMPERE_SM_REGS / (self.regs_per_thread() * self.threads()).max(1);
643        let by_smem = 102_400 / self.smem_bytes().max(1);
644        let by_thread = 1536 / self.threads().max(1);
645        by_regs.min(by_smem).min(by_thread).min(16)
646    }
647
648    /// Occupancy fraction: resident warps / 48 (Ampere's max warps per SM).
649    pub fn occupancy(&self) -> f64 {
650        (self.blocks_per_sm() * self.threads() / 32) as f64 / 48.0
651    }
652
653    /// DRAM traffic per unit of work (lower = better): global→shared reuse from the *block* tile. Each A
654    /// element is read `N/BN` times, each B element `M/BM` times, so operand bytes ∝ `1/BM + 1/BN`. EXACT byte
655    /// count — no fitted constant.
656    pub fn dram_traffic(&self) -> f64 {
657        1.0 / self.bm as f64 + 1.0 / self.bn as f64
658    }
659
660    /// Shared-memory traffic per unit of work (lower = better): shared→register reuse from the *thread* tile.
661    /// Each thread does `TM·TN` MACs per `TM + TN` shared loads, so shared bytes ∝ `1/TM + 1/TN`. EXACT byte
662    /// count — no fitted constant.
663    pub fn smem_traffic(&self) -> f64 {
664        1.0 / self.tm as f64 + 1.0 / self.tn as f64
665    }
666
667    /// Pareto dominance over the THREE physical axes — DRAM traffic ↓, SMEM traffic ↓, occupancy ↑ — with NO
668    /// weights, NO fitted constants, no assumption about which matters more. `self` dominates `other` iff it is
669    /// weakly better on all three and strictly better on at least one. A dominated tile cannot be the optimum
670    /// (under the monotonicity assumption: less traffic and more occupancy never hurt), so it need not be
671    /// measured. This is the assumption-free pruning that shrinks the space WITHOUT telling it the answer.
672    pub fn dominates(&self, other: &CudaTile) -> bool {
673        let weakly = self.dram_traffic() <= other.dram_traffic()
674            && self.smem_traffic() <= other.smem_traffic()
675            && self.occupancy() >= other.occupancy();
676        let strict = self.dram_traffic() < other.dram_traffic()
677            || self.smem_traffic() < other.smem_traffic()
678            || self.occupancy() > other.occupancy();
679        weakly && strict
680    }
681}
682
683/// The full feasible tile grid — every legal tile that fits Ampere's smem + register file. PROVABLY COMPLETE
684/// over this parameter space: nothing legal is silently dropped.
685pub fn feasible_tiles(gpu: &GpuLimits) -> Vec<CudaTile> {
686    let mut v = Vec::new();
687    for &bm in &[32usize, 64, 128, 256] {
688        for &bn in &[32usize, 64, 128, 256] {
689            for &bk in &[8usize, 16, 32] {
690                for &tm in &[4usize, 8, 16] {
691                    for &tn in &[4usize, 8, 16] {
692                        let t = CudaTile { bm, bn, bk, tm, tn };
693                        if t.is_feasible(gpu) {
694                            v.push(t);
695                        }
696                    }
697                }
698            }
699        }
700    }
701    v
702}
703
704/// CONSTRUCT the measurement frontier: the Pareto-optimal feasible tiles — those NOT dominated by any other
705/// feasible tile on the three physical axes (DRAM traffic ↓, SMEM traffic ↓, occupancy ↑). This is the honest
706/// "let it construct the answer" step: we do NOT pick a winner and we assume NO weighting — we only discard
707/// tiles that are provably worse on every axis (they cannot be optimal), then hand the survivors to the GPU,
708/// which crowns the winner by measurement. The frontier is a small superset of the true optimum; the machine,
709/// not us, resolves the un-modelable physics (bank conflicts, scheduling) that separates the survivors.
710pub fn pareto_frontier(gpu: &GpuLimits) -> Vec<CudaTile> {
711    let all = feasible_tiles(gpu);
712    all.iter()
713        .filter(|t| !all.iter().any(|o| o.dominates(t)))
714        .copied()
715        .collect()
716}
717
718// ─────────────────────── compute-primitive selection: use the tensor cores when we have them ───────────────
719/// Which compute hardware a GEMM kernel targets.
720#[derive(Debug, Clone, Copy, PartialEq, Eq)]
721pub enum ComputePrimitive {
722    /// DP4A on the CUDA cores — works on ANY shape (including GEMV), the universal fallback.
723    Dp4aCudaCore,
724    /// WMMA (IMMA) on the tensor cores — needs a tensor-core GPU AND 16-aligned M,K,N. The ~18× lever.
725    WmmaTensorCore,
726}
727
728/// SELECT the compute primitive FROM THE PROBLEM: use the tensor cores when the GPU has them AND the shape is
729/// WMMA-legal (16-aligned); otherwise fall back to DP4A on the CUDA cores. Correct by construction — we never
730/// emit a WMMA kernel on a shape or GPU that cannot run it. (Which of the two is actually *faster* when both
731/// are legal is settled by measurement on the frontier, but WMMA is the strictly-more-powerful default.)
732pub fn select_primitive(m: usize, k: usize, n: usize, has_tensor_cores: bool) -> ComputePrimitive {
733    if has_tensor_cores && wmma_legal(m, k, n) {
734        ComputePrimitive::WmmaTensorCore
735    } else {
736        ComputePrimitive::Dp4aCudaCore
737    }
738}
739
740/// Emit a full runnable GEMM program, choosing the compute primitive from the problem + hardware. When the GPU
741/// has tensor cores and the shape is 16-aligned, this emits the WMMA (tensor-core) kernel; otherwise the tiled
742/// DP4A (CUDA-core) kernel driven by `tile`. Both paths are byte-exact vs the scalar oracle.
743pub fn emit_gemm_program(
744    m: usize,
745    k: usize,
746    n: usize,
747    has_tensor_cores: bool,
748    tile: &CudaTile,
749) -> String {
750    match select_primitive(m, k, n, has_tensor_cores) {
751        ComputePrimitive::WmmaTensorCore => emit_wmma_i8_program(m, k, n),
752        ComputePrimitive::Dp4aCudaCore => emit_tiled_dp4a_program(tile, m, k, n),
753    }
754}
755
756// ─────────────────────── rung 3: pipelined WMMA — tensor cores FED FROM SHARED MEMORY, warp-tiled ──────────
757// Basic WMMA (emit_wmma_i8) reads A/B from global every step → tensor cores starve. This kernel fuses the
758// proven tiling reuse WITH the tensor cores: a BM×BN block stages A(BM×BK)+B(BK×BN) into shared memory once
759// per K-step, then WARPS_M×WARPS_N warps each compute a (BM/WARPS_M)×(BN/WARPS_N) warp-tile as a grid of int8
760// WMMA 16×16×16 fragments held in registers, reusing every shared byte across the warp-tile. This IS the
761// cuBLAS/CUTLASS kernel structure (minus cp.async double-buffering, rung 4). The block/warp shape is a tuning
762// knob — bigger tiles = more reuse per shared load, until registers/shared/threads run out.
763
764/// A pipelined-WMMA configuration: block tile `BM×BN`, K-stage `BK`, warp grid `WARPS_M×WARPS_N`.
765#[derive(Debug, Clone, Copy, PartialEq, Eq)]
766pub struct WmmaTile {
767    pub bm: usize,
768    pub bn: usize,
769    pub bk: usize,
770    pub warps_m: usize,
771    pub warps_n: usize,
772}
773
774impl WmmaTile {
775    pub fn warps(&self) -> usize {
776        self.warps_m * self.warps_n
777    }
778    pub fn threads(&self) -> usize {
779        self.warps() * 32
780    }
781    /// Warp-tile height/width (rows/cols of C each warp owns).
782    pub fn wm(&self) -> usize {
783        self.bm / self.warps_m
784    }
785    pub fn wn(&self) -> usize {
786        self.bn / self.warps_n
787    }
788    /// int8 shared-memory footprint (single-buffered): A stage + B stage.
789    pub fn smem_bytes(&self) -> usize {
790        self.bm * self.bk + self.bk * self.bn
791    }
792    /// Accumulator registers per thread ≈ (frags per warp) × 8 int32 regs per 16×16 acc fragment.
793    pub fn acc_regs(&self) -> usize {
794        (self.wm() / 16) * (self.wn() / 16) * 8
795    }
796    /// Legal iff everything divides on 16-boundaries, the warp grid tiles the block, and it fits Ampere.
797    pub fn is_legal(&self, gpu: &GpuLimits) -> bool {
798        self.bm % 16 == 0
799            && self.bn % 16 == 0
800            && self.bk % 16 == 0
801            && self.warps_m > 0
802            && self.warps_n > 0
803            && self.bm % self.warps_m == 0
804            && self.bn % self.warps_n == 0
805            && self.wm() % 16 == 0
806            && self.wn() % 16 == 0
807            && self.threads() <= gpu.max_threads
808            && self.smem_bytes() <= gpu.smem_bytes
809            && self.acc_regs() + 32 <= 255 // per-thread register cap headroom
810    }
811}
812
813/// A sensible default rung-3 config: 128×128 block, BK=32, 8 warps (2×4), so each warp owns a 64×32 warp-tile
814/// = 4×2 = 8 fragments. This is the classic WMMA GEMM shape.
815pub fn default_wmma_tile() -> WmmaTile {
816    WmmaTile {
817        bm: 128,
818        bn: 128,
819        bk: 32,
820        warps_m: 2,
821        warps_n: 4,
822    }
823}
824
825fn wmma_tiled_kernel(t: &WmmaTile) -> String {
826    wmma_tiled_kernel_for(t, MmaKind::WmmaI8)
827}
828
829/// The pipelined-WMMA kernel (shared-memory staged, warp-tiled) for any tensor-core [`MmaKind`]. bf16/f16 swap
830/// the operand type (`__nv_bfloat16`/`__half`) and the accumulator (`float`); the tiling is precision-invariant.
831/// `WmmaI8` reproduces the original int8 kernel byte-for-byte (the substitutions are no-ops there).
832fn wmma_tiled_kernel_for(t: &WmmaTile, kind: MmaKind) -> String {
833    let defs = format!(
834        "#define BM {}\n#define BN {}\n#define BK {}\n#define WARPS_M {}\n#define WARPS_N {}\n",
835        t.bm, t.bn, t.bk, t.warps_m, t.warps_n,
836    );
837    let op = kind.operand_ctype();
838    let acc = kind.acc_ctype();
839    let zero = if acc == "float" { "0.0f" } else { "0" };
840    let body = WMMA_TILED_BODY_I8
841        .replace(
842            "#include <mma.h>\n",
843            &format!("#include <mma.h>\n{}", kind.header()),
844        )
845        .replace("signed char", op)
846        .replace("int* C,", &format!("{acc}* C,"))
847        .replace(
848            "wmma::accumulator, 16, 16, 16, int>",
849            &format!("wmma::accumulator, 16, 16, 16, {acc}>"),
850        )
851        .replace(
852            "wmma::fill_fragment(acc[i][j], 0)",
853            &format!("wmma::fill_fragment(acc[i][j], {zero})"),
854        );
855    format!("{defs}{body}")
856}
857
858const WMMA_TILED_BODY_I8: &str = r#"#include <mma.h>
859using namespace nvcuda;
860#define WM (BM / WARPS_M)
861#define WN (BN / WARPS_N)
862#define NTHREAD (WARPS_M * WARPS_N * 32)
863// C(M x N) = A(M x K) . B(K x N); A,B row-major int8; int32 accumulate; tensor cores fed from shared memory.
864extern "C" __global__ void gemm_wmma_tiled(const signed char* A, const signed char* B, int* C,
865                                           int M, int N, int K) {
866    __shared__ signed char As[BM * BK];
867    __shared__ signed char Bs[BK * BN];
868    const int tid = threadIdx.x;
869    const int warpId = tid / 32;
870    const int warp_row = (warpId / WARPS_N) * WM;
871    const int warp_col = (warpId % WARPS_N) * WN;
872    const int block_row = blockIdx.y * BM;
873    const int block_col = blockIdx.x * BN;
874
875    wmma::fragment<wmma::accumulator, 16, 16, 16, int> acc[WM / 16][WN / 16];
876    #pragma unroll
877    for (int i = 0; i < WM / 16; ++i)
878        #pragma unroll
879        for (int j = 0; j < WN / 16; ++j) wmma::fill_fragment(acc[i][j], 0);
880
881    for (int k0 = 0; k0 < K; k0 += BK) {
882        for (int idx = tid; idx < BM * BK; idx += NTHREAD)
883            As[idx] = A[(block_row + idx / BK) * K + (k0 + idx % BK)];
884        for (int idx = tid; idx < BK * BN; idx += NTHREAD)
885            Bs[idx] = B[(k0 + idx / BN) * N + (block_col + idx % BN)];
886        __syncthreads();
887
888        #pragma unroll
889        for (int kk = 0; kk < BK; kk += 16) {
890            wmma::fragment<wmma::matrix_a, 16, 16, 16, signed char, wmma::row_major> a_frag[WM / 16];
891            wmma::fragment<wmma::matrix_b, 16, 16, 16, signed char, wmma::row_major> b_frag[WN / 16];
892            #pragma unroll
893            for (int i = 0; i < WM / 16; ++i)
894                wmma::load_matrix_sync(a_frag[i], As + (warp_row + i * 16) * BK + kk, BK);
895            #pragma unroll
896            for (int j = 0; j < WN / 16; ++j)
897                wmma::load_matrix_sync(b_frag[j], Bs + kk * BN + (warp_col + j * 16), BN);
898            #pragma unroll
899            for (int i = 0; i < WM / 16; ++i)
900                #pragma unroll
901                for (int j = 0; j < WN / 16; ++j)
902                    wmma::mma_sync(acc[i][j], a_frag[i], b_frag[j], acc[i][j]);
903        }
904        __syncthreads();
905    }
906
907    #pragma unroll
908    for (int i = 0; i < WM / 16; ++i)
909        #pragma unroll
910        for (int j = 0; j < WN / 16; ++j)
911            wmma::store_matrix_sync(C + (block_row + warp_row + i * 16) * N + (block_col + warp_col + j * 16),
912                                    acc[i][j], N, wmma::mem_row_major);
913}
914"#;
915
916/// Emit just the pipelined-WMMA kernel for `tile` (structural / nvcc-compile checks).
917pub fn emit_wmma_tiled_i8_for(tile: &WmmaTile) -> String {
918    wmma_tiled_kernel(tile)
919}
920
921/// Emit the pipelined-WMMA kernel with the default config.
922pub fn emit_wmma_tiled_i8() -> String {
923    wmma_tiled_kernel(&default_wmma_tile())
924}
925
926/// Checksum-matching **pipelined-WMMA** GEMM program for a tensor-core `kind` (bf16/f16) — the shared-memory,
927/// warp-tiled kernel with the matmul emitters' fill (`a[p]=(p%7)-3`, `b[p]=(p%5)-2`) and the `Σ C` integer
928/// checksum, so on int8-range operands (exact in bf16/f16) it equals the scalar oracle byte-for-byte.
929pub fn emit_wmma_tiled_program(
930    kind: MmaKind,
931    tile: &WmmaTile,
932    m: usize,
933    k: usize,
934    n: usize,
935) -> String {
936    let kernel = wmma_tiled_kernel_for(tile, kind);
937    let op = kind.operand_ctype();
938    let acc = kind.acc_ctype();
939    let conv_a = kind.host_convert("(p % 7) - 3");
940    let conv_b = kind.host_convert("(p % 5) - 2");
941    format!(
942        r#"{kernel}#include <cstdio>
943#include <cstdlib>
944#include <cmath>
945#define M {m}
946#define N {n}
947#define K {k}
948int main(void){{
949    const int MM=M,KK=K,NN=N;
950    {op}* hA=({op}*)malloc((size_t)MM*KK*sizeof({op}));
951    {op}* hB=({op}*)malloc((size_t)KK*NN*sizeof({op}));
952    for(long p=0;p<(long)MM*KK;p++) hA[p]={conv_a};
953    for(long p=0;p<(long)KK*NN;p++) hB[p]={conv_b};
954    {op} *dA,*dB; {acc}* dC;
955    if(cudaMalloc((void**)&dA,(size_t)MM*KK*sizeof({op}))||cudaMalloc((void**)&dB,(size_t)KK*NN*sizeof({op}))||cudaMalloc((void**)&dC,(size_t)MM*NN*sizeof({acc}))){{ fprintf(stderr,"malloc\n"); return 1; }}
956    cudaMemcpy(dA,hA,(size_t)MM*KK*sizeof({op}),cudaMemcpyHostToDevice);
957    cudaMemcpy(dB,hB,(size_t)KK*NN*sizeof({op}),cudaMemcpyHostToDevice);
958    dim3 grid(NN/{bn}, MM/{bm}); gemm_wmma_tiled<<<grid, {threads}>>>(dA,dB,dC,MM,NN,KK);
959    if(cudaDeviceSynchronize()!=cudaSuccess){{ fprintf(stderr,"sync\n"); return 1; }}
960    {acc}* hC=({acc}*)malloc((size_t)MM*NN*sizeof({acc}));
961    cudaMemcpy(hC,dC,(size_t)MM*NN*sizeof({acc}),cudaMemcpyDeviceToHost);
962    double sum=0; for(long t=0;t<(long)MM*NN;t++) sum+=(double)hC[t];
963    printf("%lld\n",(long long)llround(sum));
964    return 0;
965}}
966"#,
967        kernel = kernel,
968        op = op,
969        acc = acc,
970        m = m,
971        k = k,
972        n = n,
973        conv_a = conv_a,
974        conv_b = conv_b,
975        bm = tile.bm,
976        bn = tile.bn,
977        threads = tile.threads()
978    )
979}
980
981/// Timing bench for the BASIC (one-warp-per-16×16-tile) WMMA kernel at `kind` — prints `GOPS <value>`.
982pub fn emit_wmma_basic_bench(kind: MmaKind, m: usize, k: usize, n: usize) -> String {
983    let kernel = emit_wmma(kind);
984    let op = kind.operand_ctype();
985    let acc = kind.acc_ctype();
986    let tag = kind.tag();
987    gemm_timed_main(
988        &kernel,
989        op,
990        acc,
991        &format!("gemm_wmma_{tag}<<<grid, 32>>>"),
992        "dim3 grid(N/16, M/16);",
993        m,
994        k,
995        n,
996    )
997}
998
999/// Timing bench for the PIPELINED (shared-memory, warp-tiled) WMMA kernel at `kind` + `tile` — prints `GOPS`.
1000pub fn emit_wmma_tiled_bench(
1001    kind: MmaKind,
1002    tile: &WmmaTile,
1003    m: usize,
1004    k: usize,
1005    n: usize,
1006) -> String {
1007    let kernel = wmma_tiled_kernel_for(tile, kind);
1008    let op = kind.operand_ctype();
1009    let acc = kind.acc_ctype();
1010    let launch = format!("gemm_wmma_tiled<<<grid, {}>>>", tile.threads());
1011    let grid = format!("dim3 grid(N/{}, M/{});", tile.bn, tile.bm);
1012    gemm_timed_main(&kernel, op, acc, &launch, &grid, m, k, n)
1013}
1014
1015/// Shared timing harness: warm up, time 30 launches of `launch` under `grid`, print `GOPS`. Values are memset
1016/// (timing is value-independent) so both benches share one host.
1017fn gemm_timed_main(
1018    kernel: &str,
1019    op: &str,
1020    acc: &str,
1021    launch: &str,
1022    grid: &str,
1023    m: usize,
1024    k: usize,
1025    n: usize,
1026) -> String {
1027    format!(
1028        r#"{kernel}#include <cstdio>
1029int main(){{
1030    const long M={m}, K={k}, N={n};
1031    {op} *dA,*dB; {acc}* dC;
1032    cudaMalloc((void**)&dA,(size_t)M*K*sizeof({op})); cudaMalloc((void**)&dB,(size_t)K*N*sizeof({op})); cudaMalloc((void**)&dC,(size_t)M*N*sizeof({acc}));
1033    cudaMemset(dA,1,(size_t)M*K*sizeof({op})); cudaMemset(dB,1,(size_t)K*N*sizeof({op}));
1034    {grid}
1035    for(int w=0;w<5;w++) {launch}(dA,dB,dC,M,N,K);
1036    cudaDeviceSynchronize();
1037    cudaEvent_t s,e; cudaEventCreate(&s); cudaEventCreate(&e);
1038    const int ITERS=30; float total=0;
1039    for(int it=0;it<ITERS;it++){{ cudaEventRecord(s); {launch}(dA,dB,dC,M,N,K); cudaEventRecord(e); cudaEventSynchronize(e); float ms=0; cudaEventElapsedTime(&ms,s,e); total+=ms; }}
1040    double gops = 2.0*(double)M*(double)N*(double)K/((total/ITERS)/1000.0)/1e9;
1041    printf("GOPS %.1f\n", gops);
1042    return 0;
1043}}
1044"#,
1045        kernel = kernel,
1046        op = op,
1047        acc = acc,
1048        launch = launch,
1049        grid = grid,
1050        m = m,
1051        k = k,
1052        n = n
1053    )
1054}
1055
1056fn wmma_tiled_program_for(tile: &WmmaTile, m: usize, k: usize, n: usize) -> String {
1057    format!(
1058        r#"{kernel}
1059#include <cstdio>
1060#include <cstdlib>
1061int main() {{
1062    const int M = {m}, K = {k}, N = {n};
1063    signed char *hA = (signed char*)malloc((size_t)M*K), *hB = (signed char*)malloc((size_t)K*N);
1064    for (long i = 0; i < (long)M*K; ++i) hA[i] = (signed char)(((i*7+3) % 251) - 125);
1065    for (long i = 0; i < (long)K*N; ++i) hB[i] = (signed char)(((i*5+1) % 251) - 125);
1066    signed char *dA, *dB; int *dC, *hC = (int*)malloc((size_t)M*N*sizeof(int));
1067    cudaMalloc(&dA, (size_t)M*K); cudaMalloc(&dB, (size_t)K*N); cudaMalloc(&dC, (size_t)M*N*sizeof(int));
1068    cudaMemcpy(dA, hA, (size_t)M*K, cudaMemcpyHostToDevice);
1069    cudaMemcpy(dB, hB, (size_t)K*N, cudaMemcpyHostToDevice);
1070    dim3 grid(N/{bn}, M/{bm}); gemm_wmma_tiled<<<grid, {threads}>>>(dA, dB, dC, M, N, K);
1071    cudaDeviceSynchronize();
1072    cudaMemcpy(hC, dC, (size_t)M*N*sizeof(int), cudaMemcpyDeviceToHost);
1073    for (long i = 0; i < (long)M*N; ++i) printf("%d\n", hC[i]);
1074    return 0;
1075}}
1076"#,
1077        kernel = wmma_tiled_kernel(tile),
1078        m = m,
1079        k = k,
1080        n = n,
1081        bm = tile.bm,
1082        bn = tile.bn,
1083        threads = tile.threads()
1084    )
1085}
1086
1087fn wmma_tiled_bench_for(tile: &WmmaTile, m: usize, k: usize, n: usize) -> String {
1088    format!(
1089        r#"{kernel}
1090#include <cstdio>
1091#include <cstdlib>
1092int main() {{
1093    const int M = {m}, K = {k}, N = {n};
1094    signed char *dA, *dB; int *dC;
1095    cudaMalloc(&dA, (size_t)M*K); cudaMalloc(&dB, (size_t)K*N); cudaMalloc(&dC, (size_t)M*N*sizeof(int));
1096    cudaMemset(dA, 1, (size_t)M*K); cudaMemset(dB, 1, (size_t)K*N);
1097    dim3 grid(N/{bn}, M/{bm});
1098    gemm_wmma_tiled<<<grid, {threads}>>>(dA, dB, dC, M, N, K); cudaDeviceSynchronize();
1099    cudaEvent_t s, e; cudaEventCreate(&s); cudaEventCreate(&e);
1100    const int IT = 30; float total = 0;
1101    for (int it = 0; it < IT; ++it) {{
1102        cudaEventRecord(s); gemm_wmma_tiled<<<grid, {threads}>>>(dA, dB, dC, M, N, K); cudaEventRecord(e); cudaEventSynchronize(e);
1103        float ms = 0; cudaEventElapsedTime(&ms, s, e); total += ms;
1104    }}
1105    double gops = 2.0*M*N*K / ((total/IT)/1e3) / 1e9;
1106    printf("GOPS %.1f\n", gops);
1107    return 0;
1108}}
1109"#,
1110        kernel = wmma_tiled_kernel(tile),
1111        m = m,
1112        k = k,
1113        n = n,
1114        bm = tile.bm,
1115        bn = tile.bn,
1116        threads = tile.threads()
1117    )
1118}
1119
1120/// Emit a full runnable program (default config). Same deterministic fill as the other GEMM programs.
1121pub fn emit_wmma_tiled_i8_program(m: usize, k: usize, n: usize) -> String {
1122    wmma_tiled_program_for(&default_wmma_tile(), m, k, n)
1123}
1124/// Emit a timing harness (default config): 30 launches, prints `GOPS <value>`.
1125pub fn emit_wmma_tiled_i8_bench(m: usize, k: usize, n: usize) -> String {
1126    wmma_tiled_bench_for(&default_wmma_tile(), m, k, n)
1127}
1128/// Emit a program for a specific config (for the config sweep).
1129pub fn emit_wmma_tiled_i8_program_for(tile: &WmmaTile, m: usize, k: usize, n: usize) -> String {
1130    wmma_tiled_program_for(tile, m, k, n)
1131}
1132/// Emit a bench for a specific config (for the config sweep).
1133pub fn emit_wmma_tiled_i8_bench_for(tile: &WmmaTile, m: usize, k: usize, n: usize) -> String {
1134    wmma_tiled_bench_for(tile, m, k, n)
1135}
1136
1137// ─────────────────────── rung 4: cp.async double-buffering — pipeline global loads behind compute ──────────
1138// Rung 3 serializes load → __syncthreads → compute → __syncthreads. cp.async (Ampere) streams the NEXT K-tile
1139// from global into a second shared buffer WHILE the tensor cores chew the current one — the WMMA units stop
1140// starving on memory. 16-byte vectorized async copies (needs BK%16==0 for A rows, BN%16==0 for B rows), two
1141// shared buffers, __pipeline_commit / __pipeline_wait_prior. This is the last big PUBLIC lever before the
1142// hand-tuned-SASS moat.
1143
1144impl WmmaTile {
1145    /// cp.async needs 16-byte contiguous chunks: A rows are `BK` wide, B rows `BN` wide.
1146    pub fn cpasync_legal(&self, gpu: &GpuLimits) -> bool {
1147        self.is_legal(gpu)
1148            && self.bk % 16 == 0
1149            && self.bn % 16 == 0
1150            && (self.bm * self.bk) % 16 == 0
1151            && 2 * self.smem_bytes() <= gpu.smem_bytes // double-buffered
1152    }
1153}
1154
1155fn wmma_cpasync_kernel(t: &WmmaTile) -> String {
1156    wmma_cpasync_kernel_staged(t, 2)
1157}
1158
1159fn wmma_cpasync_kernel_staged(t: &WmmaTile, stages: usize) -> String {
1160    wmma_cpasync_kernel_staged_for(t, stages, MmaKind::WmmaI8)
1161}
1162
1163/// cp.async double-buffered WMMA for any tensor-core [`MmaKind`]. bf16/f16 swap the operand/accumulator AND the
1164/// async copy's elements-per-16-byte-chunk (`EPC` = 16 for 1-byte int8, 8 for 2-byte bf16/f16 — the `cp.async`
1165/// copy is always 16 bytes). `WmmaI8` reproduces the int8 kernel semantics exactly (`EPC = 16`).
1166fn wmma_cpasync_kernel_staged_for(t: &WmmaTile, stages: usize, kind: MmaKind) -> String {
1167    let op = kind.operand_ctype();
1168    let acc = kind.acc_ctype();
1169    let zero = if acc == "float" { "0.0f" } else { "0" };
1170    let epc = if op == "signed char" { 16 } else { 8 }; // 16 bytes / sizeof(operand)
1171    let defs = format!(
1172        "#define BM {}\n#define BN {}\n#define BK {}\n#define WARPS_M {}\n#define WARPS_N {}\n#define STAGES {}\n#define EPC {}\n",
1173        t.bm, t.bn, t.bk, t.warps_m, t.warps_n, stages, epc,
1174    );
1175    let body = CPASYNC_BODY_I8
1176        .replace(
1177            "#include <cuda_pipeline.h>\n",
1178            &format!("#include <cuda_pipeline.h>\n{}", kind.header()),
1179        )
1180        .replace("signed char", op)
1181        .replace("int* C,", &format!("{acc}* C,"))
1182        .replace(
1183            "wmma::accumulator, 16, 16, 16, int>",
1184            &format!("wmma::accumulator, 16, 16, 16, {acc}>"),
1185        )
1186        .replace(
1187            "wmma::fill_fragment(acc[i][j], 0)",
1188            &format!("wmma::fill_fragment(acc[i][j], {zero})"),
1189        )
1190        .replace("(BM * BK) / 16", "(BM * BK) / EPC")
1191        .replace("(BK * BN) / 16", "(BK * BN) / EPC")
1192        .replace(
1193            "(idx * 16) / BK, c = (idx * 16) % BK",
1194            "(idx * EPC) / BK, c = (idx * EPC) % BK",
1195        )
1196        .replace(
1197            "(idx * 16) / BN, c = (idx * 16) % BN",
1198            "(idx * EPC) / BN, c = (idx * EPC) % BN",
1199        );
1200    format!("{defs}{body}")
1201}
1202
1203const CPASYNC_BODY_I8: &str = r#"#include <mma.h>
1204#include <cuda_pipeline.h>
1205using namespace nvcuda;
1206#define WM (BM / WARPS_M)
1207#define WN (BN / WARPS_N)
1208#define NTHREAD (WARPS_M * WARPS_N * 32)
1209extern "C" __global__ void gemm_wmma_cpasync(const signed char* A, const signed char* B, int* C,
1210                                             int M, int N, int K) {
1211    __shared__ signed char As[STAGES][BM * BK];
1212    __shared__ signed char Bs[STAGES][BK * BN];
1213    const int tid = threadIdx.x;
1214    const int warpId = tid / 32;
1215    const int warp_row = (warpId / WARPS_N) * WM;
1216    const int warp_col = (warpId % WARPS_N) * WN;
1217    const int block_row = blockIdx.y * BM;
1218    const int block_col = blockIdx.x * BN;
1219    const int nstages = K / BK;
1220
1221    wmma::fragment<wmma::accumulator, 16, 16, 16, int> acc[WM / 16][WN / 16];
1222    #pragma unroll
1223    for (int i = 0; i < WM / 16; ++i)
1224        #pragma unroll
1225        for (int j = 0; j < WN / 16; ++j) wmma::fill_fragment(acc[i][j], 0);
1226
1227    // stream stage `st` (K offset `st*BK`) into shared buffer `buf` with 16-byte async copies.
1228    #define LOAD_STAGE(buf, st) do { \
1229        int _k0 = (st) * BK; \
1230        for (int idx = tid; idx < (BM * BK) / 16; idx += NTHREAD) { \
1231            int r = (idx * 16) / BK, c = (idx * 16) % BK; \
1232            __pipeline_memcpy_async(&As[buf][r * BK + c], &A[(block_row + r) * K + _k0 + c], 16); \
1233        } \
1234        for (int idx = tid; idx < (BK * BN) / 16; idx += NTHREAD) { \
1235            int r = (idx * 16) / BN, c = (idx * 16) % BN; \
1236            __pipeline_memcpy_async(&Bs[buf][r * BN + c], &B[(_k0 + r) * N + block_col + c], 16); \
1237        } \
1238        __pipeline_commit(); \
1239    } while (0)
1240
1241    // prologue: prefetch the first STAGES-1 K-tiles so STAGES-1 async groups are always in flight.
1242    #pragma unroll
1243    for (int i = 0; i < STAGES - 1; ++i) { if (i < nstages) LOAD_STAGE(i, i); else __pipeline_commit(); }
1244
1245    for (int s = 0; s < nstages; ++s) {
1246        __pipeline_wait_prior(STAGES - 2); // block until stage s's buffer has landed
1247        __syncthreads();
1248
1249        int buf = s % STAGES;
1250        #pragma unroll
1251        for (int kk = 0; kk < BK; kk += 16) {
1252            wmma::fragment<wmma::matrix_a, 16, 16, 16, signed char, wmma::row_major> a_frag[WM / 16];
1253            wmma::fragment<wmma::matrix_b, 16, 16, 16, signed char, wmma::row_major> b_frag[WN / 16];
1254            #pragma unroll
1255            for (int i = 0; i < WM / 16; ++i)
1256                wmma::load_matrix_sync(a_frag[i], As[buf] + (warp_row + i * 16) * BK + kk, BK);
1257            #pragma unroll
1258            for (int j = 0; j < WN / 16; ++j)
1259                wmma::load_matrix_sync(b_frag[j], Bs[buf] + kk * BN + (warp_col + j * 16), BN);
1260            #pragma unroll
1261            for (int i = 0; i < WM / 16; ++i)
1262                #pragma unroll
1263                for (int j = 0; j < WN / 16; ++j)
1264                    wmma::mma_sync(acc[i][j], a_frag[i], b_frag[j], acc[i][j]);
1265        }
1266        __syncthreads();
1267        // prefetch the tile STAGES-1 ahead into its rotating buffer, keeping the pipeline full.
1268        int nxt = s + STAGES - 1;
1269        if (nxt < nstages) LOAD_STAGE(nxt % STAGES, nxt); else __pipeline_commit();
1270    }
1271
1272    #pragma unroll
1273    for (int i = 0; i < WM / 16; ++i)
1274        #pragma unroll
1275        for (int j = 0; j < WN / 16; ++j)
1276            wmma::store_matrix_sync(C + (block_row + warp_row + i * 16) * N + (block_col + warp_col + j * 16),
1277                                    acc[i][j], N, wmma::mem_row_major);
1278}
1279"#;
1280
1281pub fn emit_wmma_cpasync_i8_for(tile: &WmmaTile) -> String {
1282    wmma_cpasync_kernel(tile)
1283}
1284
1285fn wmma_cpasync_harness(
1286    tile: &WmmaTile,
1287    stages: usize,
1288    m: usize,
1289    k: usize,
1290    n: usize,
1291    bench: bool,
1292) -> String {
1293    let launch = format!(
1294        "dim3 grid(N/{}, M/{}); gemm_wmma_cpasync<<<grid, {}>>>",
1295        tile.bn,
1296        tile.bm,
1297        tile.threads()
1298    );
1299    if bench {
1300        format!(
1301            r#"{kernel}
1302#include <cstdio>
1303#include <cstdlib>
1304int main() {{
1305    const int M = {m}, K = {k}, N = {n};
1306    signed char *dA, *dB; int *dC;
1307    cudaMalloc(&dA, (size_t)M*K); cudaMalloc(&dB, (size_t)K*N); cudaMalloc(&dC, (size_t)M*N*sizeof(int));
1308    // A,B all ones → every C[i][j] must equal K. In-harness correctness GUARD: a kernel that races (computes
1309    // garbage fast) or fails to launch can never print a GOPS number.
1310    cudaMemset(dA, 1, (size_t)M*K); cudaMemset(dB, 1, (size_t)K*N);
1311    {launch}(dA, dB, dC, M, N, K);
1312    cudaError_t err = cudaDeviceSynchronize();
1313    if (err != cudaSuccess) {{ printf("LAUNCH_ERROR %s\n", cudaGetErrorString(err)); return 1; }}
1314    int *hC = (int*)malloc((size_t)M*N*sizeof(int));
1315    cudaMemcpy(hC, dC, (size_t)M*N*sizeof(int), cudaMemcpyDeviceToHost);
1316    long bad = 0; long step = ((long)M*N) / 4096 + 1;
1317    for (long i = 0; i < (long)M*N; i += step) if (hC[i] != K) {{ if (bad < 3) printf("WRONG at %ld: got %d want %d\n", i, hC[i], K); bad++; }}
1318    if (bad) {{ printf("INCORRECT %ld samples\n", bad); return 1; }}
1319    // Per-iteration SYNCED timing: each launch completes before the next is queued. Firing async launches in a
1320    // tight loop and bracketing with events under-times badly (rapid launches error/no-op while a stale
1321    // correct C sits in place) — this is the honest method.
1322    cudaEvent_t s, e; cudaEventCreate(&s); cudaEventCreate(&e);
1323    const int IT = 30; float total = 0;
1324    for (int it = 0; it < IT; ++it) {{
1325        cudaEventRecord(s); {launch}(dA, dB, dC, M, N, K); cudaEventRecord(e); cudaEventSynchronize(e);
1326        float ms = 0; cudaEventElapsedTime(&ms, s, e); total += ms;
1327    }}
1328    cudaError_t le = cudaGetLastError();
1329    if (le != cudaSuccess) {{ printf("LOOP_ERROR %s\n", cudaGetErrorString(le)); return 1; }}
1330    printf("GOPS %.1f\n", 2.0*M*N*K / ((total/IT)/1e3) / 1e9);
1331    return 0;
1332}}
1333"#,
1334            kernel = wmma_cpasync_kernel_staged(tile, stages),
1335            m = m,
1336            k = k,
1337            n = n,
1338            launch = launch
1339        )
1340    } else {
1341        format!(
1342            r#"{kernel}
1343#include <cstdio>
1344#include <cstdlib>
1345int main() {{
1346    const int M = {m}, K = {k}, N = {n};
1347    signed char *hA = (signed char*)malloc((size_t)M*K), *hB = (signed char*)malloc((size_t)K*N);
1348    for (long i = 0; i < (long)M*K; ++i) hA[i] = (signed char)(((i*7+3) % 251) - 125);
1349    for (long i = 0; i < (long)K*N; ++i) hB[i] = (signed char)(((i*5+1) % 251) - 125);
1350    signed char *dA, *dB; int *dC, *hC = (int*)malloc((size_t)M*N*sizeof(int));
1351    cudaMalloc(&dA, (size_t)M*K); cudaMalloc(&dB, (size_t)K*N); cudaMalloc(&dC, (size_t)M*N*sizeof(int));
1352    cudaMemcpy(dA, hA, (size_t)M*K, cudaMemcpyHostToDevice);
1353    cudaMemcpy(dB, hB, (size_t)K*N, cudaMemcpyHostToDevice);
1354    {launch}(dA, dB, dC, M, N, K); cudaDeviceSynchronize();
1355    cudaMemcpy(hC, dC, (size_t)M*N*sizeof(int), cudaMemcpyDeviceToHost);
1356    for (long i = 0; i < (long)M*N; ++i) printf("%d\n", hC[i]);
1357    return 0;
1358}}
1359"#,
1360            kernel = wmma_cpasync_kernel_staged(tile, stages),
1361            m = m,
1362            k = k,
1363            n = n,
1364            launch = launch
1365        )
1366    }
1367}
1368
1369pub fn emit_wmma_cpasync_i8_program_for(tile: &WmmaTile, m: usize, k: usize, n: usize) -> String {
1370    wmma_cpasync_harness(tile, 2, m, k, n, false)
1371}
1372pub fn emit_wmma_cpasync_i8_bench_for(tile: &WmmaTile, m: usize, k: usize, n: usize) -> String {
1373    wmma_cpasync_harness(tile, 2, m, k, n, true)
1374}
1375/// Program/bench for a specific pipeline DEPTH (`stages` shared buffers). 2 = double-buffer; 3-4 = deeper
1376/// pipeline that keeps the tensor cores fed further ahead (needs `stages × smem` to fit).
1377pub fn emit_wmma_cpasync_i8_program_staged(
1378    tile: &WmmaTile,
1379    stages: usize,
1380    m: usize,
1381    k: usize,
1382    n: usize,
1383) -> String {
1384    wmma_cpasync_harness(tile, stages, m, k, n, false)
1385}
1386pub fn emit_wmma_cpasync_i8_bench_staged(
1387    tile: &WmmaTile,
1388    stages: usize,
1389    m: usize,
1390    k: usize,
1391    n: usize,
1392) -> String {
1393    wmma_cpasync_harness(tile, stages, m, k, n, true)
1394}
1395
1396/// Checksum-matching cp.async GEMM program for a tensor-core `kind` (bf16/f16) — Σ C ≡ the scalar oracle on
1397/// int8-range operands (exact in bf16/f16, wide f32 accumulate). Launches the double-buffered `gemm_wmma_cpasync`.
1398pub fn emit_wmma_cpasync_program(
1399    kind: MmaKind,
1400    tile: &WmmaTile,
1401    stages: usize,
1402    m: usize,
1403    k: usize,
1404    n: usize,
1405) -> String {
1406    let kernel = wmma_cpasync_kernel_staged_for(tile, stages, kind);
1407    let op = kind.operand_ctype();
1408    let acc = kind.acc_ctype();
1409    let conv_a = kind.host_convert("(p % 7) - 3");
1410    let conv_b = kind.host_convert("(p % 5) - 2");
1411    format!(
1412        r#"{kernel}#include <cstdio>
1413#include <cstdlib>
1414#include <cmath>
1415#define M {m}
1416#define N {n}
1417#define K {k}
1418int main(void){{
1419    const int MM=M,KK=K,NN=N;
1420    {op}* hA=({op}*)malloc((size_t)MM*KK*sizeof({op}));
1421    {op}* hB=({op}*)malloc((size_t)KK*NN*sizeof({op}));
1422    for(long p=0;p<(long)MM*KK;p++) hA[p]={conv_a};
1423    for(long p=0;p<(long)KK*NN;p++) hB[p]={conv_b};
1424    {op} *dA,*dB; {acc}* dC;
1425    if(cudaMalloc((void**)&dA,(size_t)MM*KK*sizeof({op}))||cudaMalloc((void**)&dB,(size_t)KK*NN*sizeof({op}))||cudaMalloc((void**)&dC,(size_t)MM*NN*sizeof({acc}))){{ fprintf(stderr,"malloc\n"); return 1; }}
1426    cudaMemcpy(dA,hA,(size_t)MM*KK*sizeof({op}),cudaMemcpyHostToDevice);
1427    cudaMemcpy(dB,hB,(size_t)KK*NN*sizeof({op}),cudaMemcpyHostToDevice);
1428    dim3 grid(NN/{bn}, MM/{bm}); gemm_wmma_cpasync<<<grid, {threads}>>>(dA,dB,dC,MM,NN,KK);
1429    if(cudaDeviceSynchronize()!=cudaSuccess){{ fprintf(stderr,"sync\n"); return 1; }}
1430    {acc}* hC=({acc}*)malloc((size_t)MM*NN*sizeof({acc}));
1431    cudaMemcpy(hC,dC,(size_t)MM*NN*sizeof({acc}),cudaMemcpyDeviceToHost);
1432    double sum=0; for(long t=0;t<(long)MM*NN;t++) sum+=(double)hC[t];
1433    printf("%lld\n",(long long)llround(sum));
1434    return 0;
1435}}
1436"#,
1437        kernel = kernel,
1438        op = op,
1439        acc = acc,
1440        m = m,
1441        k = k,
1442        n = n,
1443        conv_a = conv_a,
1444        conv_b = conv_b,
1445        bm = tile.bm,
1446        bn = tile.bn,
1447        threads = tile.threads()
1448    )
1449}
1450
1451/// Timing bench for the cp.async double-buffered WMMA kernel at `kind` + `tile` + `stages` — prints `GOPS`.
1452pub fn emit_wmma_cpasync_bench(
1453    kind: MmaKind,
1454    tile: &WmmaTile,
1455    stages: usize,
1456    m: usize,
1457    k: usize,
1458    n: usize,
1459) -> String {
1460    let kernel = wmma_cpasync_kernel_staged_for(tile, stages, kind);
1461    gemm_timed_main(
1462        &kernel,
1463        kind.operand_ctype(),
1464        kind.acc_ctype(),
1465        &format!("gemm_wmma_cpasync<<<grid, {}>>>", tile.threads()),
1466        &format!("dim3 grid(N/{}, M/{});", tile.bn, tile.bm),
1467        m,
1468        k,
1469        n,
1470    )
1471}
1472/// A `stages`-deep pipeline fits iff `stages` shared buffers (each = the single-stage footprint) fit smem.
1473pub fn cpasync_stages_fit(tile: &WmmaTile, stages: usize, gpu: &GpuLimits) -> bool {
1474    tile.bk % 16 == 0
1475        && tile.bn % 16 == 0
1476        && (tile.bm * tile.bk) % 16 == 0
1477        && tile.bm % 16 == 0
1478        && tile.bn % 16 == 0
1479        && tile.wm() % 16 == 0
1480        && tile.wn() % 16 == 0
1481        && tile.warps_m > 0
1482        && tile.warps_n > 0
1483        && tile.bm % tile.warps_m == 0
1484        && tile.bn % tile.warps_n == 0
1485        && tile.threads() <= gpu.max_threads
1486        && stages * (tile.bm * tile.bk + tile.bk * tile.bn) <= gpu.smem_bytes
1487}
1488
1489// ─────────────────────── the LAYOUT knob — a first-class frontier dimension with real tradeoffs ────────────
1490// The int8 tensor cores (mma.sync.s8) have a NATIVE operand layout: TN (op(A)=A^T, op(B)=B — the contraction
1491// dim K contiguous for both, so no on-chip transpose/shuffle). cuBLAS is ~3x faster in TN than in NN for
1492// exactly this reason (MEASURED on this box: TN 71,140 vs NN 24,515 GOPS). So LAYOUT is not cosmetic — it is
1493// a knob with a 3x tradeoff, and it belongs on the tuning frontier alongside block/warp/pipeline/primitive.
1494// Honest state: our WMMA kernel runs NN and already BEATS cuBLAS's NN fallback (32,299 vs 24,515); realizing
1495// cuBLAS's TN *speed* needs a TN-native mma.sync + ldmatrix kernel (the next build) — this enum makes layout a
1496// selectable dimension so that kernel slots in as another frontier point rather than a special case.
1497
1498/// GEMM operand layout (BLAS transpose convention). The int8 tensor cores are fastest in `Tn`.
1499#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1500pub enum GemmLayout {
1501    /// A row-major, B row-major — the layout most callers hand you. Universal; tensor cores must rearrange.
1502    Nn,
1503    /// A row-major, B col-major.
1504    Nt,
1505    /// A col-major (= A^T), B row-major — the mma.sync.s8 NATIVE layout. Fastest on the tensor cores.
1506    Tn,
1507    /// A col-major, B col-major.
1508    Tt,
1509}
1510
1511impl GemmLayout {
1512    /// Whether this layout is the tensor cores' native contraction-contiguous layout (zero rearrange cost).
1513    pub fn is_tensor_core_native(&self) -> bool {
1514        matches!(self, GemmLayout::Tn)
1515    }
1516    /// All four layouts — the layout axis of the frontier.
1517    pub fn all() -> [GemmLayout; 4] {
1518        [
1519            GemmLayout::Nn,
1520            GemmLayout::Nt,
1521            GemmLayout::Tn,
1522            GemmLayout::Tt,
1523        ]
1524    }
1525}
1526
1527/// Select the layout to emit FROM THE PROBLEM: if the caller's data is already TN (or we are free to choose
1528/// the staging), prefer the tensor-core-native `Tn` for max throughput; if the data is fixed as `Nn` and a
1529/// transpose would cost more than it saves, stay in `Nn` (where our kernel already beats cuBLAS). `caller_is_tn`
1530/// = the operands are already available contraction-contiguous. This is the tradeoff knob, not a fixed choice.
1531pub fn select_layout(caller_is_tn: bool) -> GemmLayout {
1532    if caller_is_tn {
1533        GemmLayout::Tn
1534    } else {
1535        GemmLayout::Nn
1536    }
1537}
1538
1539// ─────────────────────── rung 6: TN-native mma.sync.s8 — feed the tensor cores their native layout ─────────
1540// cuBLAS's 72,511 GOPS comes from the int8 tensor cores' NATIVE instruction `mma.sync.aligned.m16n8k16.s8`
1541// (below the nvcuda::wmma wrapper), with operands placed in the exact register layout the hardware wants (A
1542// row-major m16k16, B col-major k16n8). We stage A/B in shared, then each warp loads the mma fragments BY HAND
1543// per the PTX fragment map and issues mma.sync. Correctness is checked against the VARIED-fill oracle (all-ones
1544// cannot catch a fragment permutation). Fixed 64×64 block, BK=16, 2×2 warps, each warp a 32×32 warp-tile
1545// (2 m-tiles × 4 n-tiles of m16n8).
1546
1547fn mma_tn_kernel() -> &'static str {
1548    r#"#define BM 64
1549#define BN 64
1550#define BK 16
1551#define WARPS_M 2
1552#define WARPS_N 2
1553#define WM (BM / WARPS_M)
1554#define WN (BN / WARPS_N)
1555#define NTHREAD (WARPS_M * WARPS_N * 32)
1556#define PK4(p,st) (((p)[0]&0xff)|(((p)[st]&0xff)<<8)|(((p)[2*(st)]&0xff)<<16)|(((p)[3*(st)]&0xff)<<24))
1557// C(MxN)=A(MxK).B(KxN), int8, int32 accumulate, via the native mma.sync.aligned.m16n8k16.row.col.s32.s8.s8.s32.
1558extern "C" __global__ void gemm_mma_tn(const signed char* A, const signed char* B, int* C, int M, int N, int K) {
1559    __shared__ signed char As[BM * BK];
1560    __shared__ signed char Bs[BK * BN];
1561    const int tid = threadIdx.x, warpId = tid / 32, lane = tid % 32;
1562    const int gid = lane / 4, tg = lane % 4;          // groupID (0..7), thread-in-group (0..3)
1563    const int warp_row = (warpId / WARPS_N) * WM, warp_col = (warpId % WARPS_N) * WN;
1564    const int block_row = blockIdx.y * BM, block_col = blockIdx.x * BN;
1565
1566    int c[WM / 16][WN / 8][4];
1567    #pragma unroll
1568    for (int mi = 0; mi < WM / 16; ++mi)
1569        #pragma unroll
1570        for (int ni = 0; ni < WN / 8; ++ni) { c[mi][ni][0]=c[mi][ni][1]=c[mi][ni][2]=c[mi][ni][3]=0; }
1571
1572    for (int k0 = 0; k0 < K; k0 += BK) {
1573        for (int idx = tid; idx < BM * BK; idx += NTHREAD) { int r = idx / BK, cc = idx % BK; As[idx] = A[(block_row + r) * K + k0 + cc]; }
1574        for (int idx = tid; idx < BK * BN; idx += NTHREAD) { int r = idx / BN, cc = idx % BN; Bs[idx] = B[(k0 + r) * N + block_col + cc]; }
1575        __syncthreads();
1576
1577        #pragma unroll
1578        for (int kk = 0; kk < BK; kk += 16) {
1579            #pragma unroll
1580            for (int mi = 0; mi < WM / 16; ++mi) {
1581                signed char* Ap0 = &As[(warp_row + mi * 16 + gid) * BK + kk + tg * 4];
1582                signed char* Ap1 = &As[(warp_row + mi * 16 + gid + 8) * BK + kk + tg * 4];
1583                int a0 = PK4(Ap0, 1), a1 = PK4(Ap1, 1);
1584                #pragma unroll
1585                for (int ni = 0; ni < WN / 8; ++ni) {
1586                    signed char* Bp = &Bs[(kk + tg * 4) * BN + (warp_col + ni * 8 + gid)];
1587                    int b0 = PK4(Bp, BN);
1588                    asm volatile(
1589                        "mma.sync.aligned.m16n8k16.row.col.s32.s8.s8.s32 {%0,%1,%2,%3},{%4,%5},{%6},{%0,%1,%2,%3};"
1590                        : "+r"(c[mi][ni][0]), "+r"(c[mi][ni][1]), "+r"(c[mi][ni][2]), "+r"(c[mi][ni][3])
1591                        : "r"(a0), "r"(a1), "r"(b0));
1592                }
1593            }
1594        }
1595        __syncthreads();
1596    }
1597
1598    #pragma unroll
1599    for (int mi = 0; mi < WM / 16; ++mi)
1600        #pragma unroll
1601        for (int ni = 0; ni < WN / 8; ++ni) {
1602            int rb = block_row + warp_row + mi * 16, cb = block_col + warp_col + ni * 8;
1603            C[(rb + gid) * N + cb + tg * 2]     = c[mi][ni][0];
1604            C[(rb + gid) * N + cb + tg * 2 + 1] = c[mi][ni][1];
1605            C[(rb + gid + 8) * N + cb + tg * 2]     = c[mi][ni][2];
1606            C[(rb + gid + 8) * N + cb + tg * 2 + 1] = c[mi][ni][3];
1607        }
1608}
1609"#
1610}
1611
1612pub fn emit_mma_tn_i8() -> String {
1613    mma_tn_kernel().to_string()
1614}
1615
1616pub fn emit_mma_tn_i8_program(m: usize, k: usize, n: usize) -> String {
1617    format!(
1618        r#"{kernel}
1619#include <cstdio>
1620#include <cstdlib>
1621int main() {{
1622    const int M={m}, K={k}, N={n};
1623    signed char *hA=(signed char*)malloc((size_t)M*K), *hB=(signed char*)malloc((size_t)K*N);
1624    for (long i=0;i<(long)M*K;++i) hA[i]=(signed char)(((i*7+3)%251)-125);
1625    for (long i=0;i<(long)K*N;++i) hB[i]=(signed char)(((i*5+1)%251)-125);
1626    signed char *dA,*dB; int *dC,*hC=(int*)malloc((size_t)M*N*4);
1627    cudaMalloc(&dA,(size_t)M*K); cudaMalloc(&dB,(size_t)K*N); cudaMalloc(&dC,(size_t)M*N*4);
1628    cudaMemcpy(dA,hA,(size_t)M*K,cudaMemcpyHostToDevice); cudaMemcpy(dB,hB,(size_t)K*N,cudaMemcpyHostToDevice);
1629    dim3 grid(N/64, M/64); gemm_mma_tn<<<grid,128>>>(dA,dB,dC,M,N,K); cudaDeviceSynchronize();
1630    cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
1631    for (long i=0;i<(long)M*N;++i) printf("%d\n", hC[i]);
1632    return 0;
1633}}
1634"#,
1635        kernel = mma_tn_kernel(),
1636        m = m,
1637        k = k,
1638        n = n
1639    )
1640}
1641
1642pub fn emit_mma_tn_i8_bench(m: usize, k: usize, n: usize) -> String {
1643    format!(
1644        r#"{kernel}
1645#include <cstdio>
1646#include <cstdlib>
1647int main() {{
1648    const int M={m}, K={k}, N={n};
1649    signed char *dA,*dB; int *dC;
1650    cudaMalloc(&dA,(size_t)M*K); cudaMalloc(&dB,(size_t)K*N); cudaMalloc(&dC,(size_t)M*N*4);
1651    cudaMemset(dA,1,(size_t)M*K); cudaMemset(dB,1,(size_t)K*N);
1652    dim3 grid(N/64,M/64);
1653    gemm_mma_tn<<<grid,128>>>(dA,dB,dC,M,N,K);
1654    cudaError_t err=cudaDeviceSynchronize(); if(err){{printf("LAUNCH_ERROR %s\n",cudaGetErrorString(err));return 1;}}
1655    int *hC=(int*)malloc((size_t)M*N*4); cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
1656    long step=((long)M*N)/4096+1; for(long i=0;i<(long)M*N;i+=step) if(hC[i]!=K){{printf("INCORRECT at %ld: %d!=%d\n",i,hC[i],K);return 1;}}
1657    cudaEvent_t s,e; cudaEventCreate(&s); cudaEventCreate(&e); int IT=30; float total=0;
1658    for(int it=0;it<IT;it++){{ cudaEventRecord(s); gemm_mma_tn<<<grid,128>>>(dA,dB,dC,M,N,K); cudaEventRecord(e); cudaEventSynchronize(e); float ms=0; cudaEventElapsedTime(&ms,s,e); total+=ms; }}
1659    printf("GOPS %.1f\n", 2.0*M*N*K/((total/IT)/1e3)/1e9);
1660    return 0;
1661}}
1662"#,
1663        kernel = mma_tn_kernel(),
1664        m = m,
1665        k = k,
1666        n = n
1667    )
1668}
1669
1670// ─────────────────────── rung 6b: PIPELINED mma.sync.s8 TN — proven fragment map + proven cp.async ─────────
1671// Fuses the two proven pieces: the correct mma.sync.aligned.m16n8k16.s8 fragment map (rung 6, oracle-verified)
1672// + cp.async double-buffered staging (rung 4). The naive rung-6 starved because the global loads were on the
1673// critical path; here the next K-tile streams while the tensor cores compute. Block 128×64, BK=32, 2×2 warps
1674// (WM=64, WN=32 → 4×4 = 16 m16n8 tiles/warp). All-ones guard in the bench; varied-fill oracle in the program.
1675fn mma_tn_cpasync_kernel() -> &'static str {
1676    r#"#include <cuda_pipeline.h>
1677#define BM 128
1678#define BN 64
1679#define BK 32
1680#define WARPS_M 2
1681#define WARPS_N 2
1682#define WM (BM / WARPS_M)
1683#define WN (BN / WARPS_N)
1684#define NTHREAD (WARPS_M * WARPS_N * 32)
1685#define PK4(p,st) (((p)[0]&0xff)|(((p)[st]&0xff)<<8)|(((p)[2*(st)]&0xff)<<16)|(((p)[3*(st)]&0xff)<<24))
1686extern "C" __global__ void gemm_mma_tn_cpasync(const signed char* A, const signed char* B, int* C, int M, int N, int K) {
1687    __shared__ signed char As[2][BM * BK];
1688    __shared__ signed char Bs[2][BK * BN];
1689    const int tid = threadIdx.x, warpId = tid / 32, lane = tid % 32;
1690    const int gid = lane / 4, tg = lane % 4;
1691    const int warp_row = (warpId / WARPS_N) * WM, warp_col = (warpId % WARPS_N) * WN;
1692    const int block_row = blockIdx.y * BM, block_col = blockIdx.x * BN;
1693    const int nstages = K / BK;
1694
1695    int c[WM / 16][WN / 8][4];
1696    #pragma unroll
1697    for (int mi = 0; mi < WM / 16; ++mi)
1698        #pragma unroll
1699        for (int ni = 0; ni < WN / 8; ++ni) { c[mi][ni][0]=c[mi][ni][1]=c[mi][ni][2]=c[mi][ni][3]=0; }
1700
1701    #define LOAD_STAGE(buf, st) do { int _k0 = (st) * BK; \
1702        for (int idx = tid; idx < (BM * BK) / 16; idx += NTHREAD) { int r=(idx*16)/BK, cc=(idx*16)%BK; \
1703            __pipeline_memcpy_async(&As[buf][r*BK+cc], &A[(block_row+r)*K + _k0+cc], 16); } \
1704        for (int idx = tid; idx < (BK * BN) / 16; idx += NTHREAD) { int r=(idx*16)/BN, cc=(idx*16)%BN; \
1705            __pipeline_memcpy_async(&Bs[buf][r*BN+cc], &B[(_k0+r)*N + block_col+cc], 16); } \
1706        __pipeline_commit(); } while (0)
1707
1708    LOAD_STAGE(0, 0);
1709    for (int s = 0; s < nstages; ++s) {
1710        if (s + 1 < nstages) LOAD_STAGE((s + 1) & 1, s + 1);
1711        __pipeline_wait_prior(s + 1 < nstages ? 1 : 0);
1712        __syncthreads();
1713        int buf = s & 1;
1714        #pragma unroll
1715        for (int kk = 0; kk < BK; kk += 16) {
1716            #pragma unroll
1717            for (int mi = 0; mi < WM / 16; ++mi) {
1718                signed char* Ap0 = &As[buf][(warp_row + mi*16 + gid) * BK + kk + tg*4];
1719                signed char* Ap1 = &As[buf][(warp_row + mi*16 + gid + 8) * BK + kk + tg*4];
1720                int a0 = PK4(Ap0, 1), a1 = PK4(Ap1, 1);
1721                #pragma unroll
1722                for (int ni = 0; ni < WN / 8; ++ni) {
1723                    signed char* Bp = &Bs[buf][(kk + tg*4) * BN + (warp_col + ni*8 + gid)];
1724                    int b0 = PK4(Bp, BN);
1725                    asm volatile("mma.sync.aligned.m16n8k16.row.col.s32.s8.s8.s32 {%0,%1,%2,%3},{%4,%5},{%6},{%0,%1,%2,%3};"
1726                        : "+r"(c[mi][ni][0]),"+r"(c[mi][ni][1]),"+r"(c[mi][ni][2]),"+r"(c[mi][ni][3])
1727                        : "r"(a0),"r"(a1),"r"(b0));
1728                }
1729            }
1730        }
1731        __syncthreads();
1732    }
1733    #pragma unroll
1734    for (int mi = 0; mi < WM / 16; ++mi)
1735        #pragma unroll
1736        for (int ni = 0; ni < WN / 8; ++ni) {
1737            int rb = block_row + warp_row + mi*16, cb = block_col + warp_col + ni*8;
1738            C[(rb+gid)*N + cb+tg*2]       = c[mi][ni][0];
1739            C[(rb+gid)*N + cb+tg*2+1]     = c[mi][ni][1];
1740            C[(rb+gid+8)*N + cb+tg*2]     = c[mi][ni][2];
1741            C[(rb+gid+8)*N + cb+tg*2+1]   = c[mi][ni][3];
1742        }
1743}
1744"#
1745}
1746
1747pub fn emit_mma_tn_cpasync_program(m: usize, k: usize, n: usize) -> String {
1748    format!(
1749        r#"{kernel}
1750#include <cstdio>
1751#include <cstdlib>
1752int main() {{ const int M={m},K={k},N={n};
1753    signed char *hA=(signed char*)malloc((size_t)M*K),*hB=(signed char*)malloc((size_t)K*N);
1754    for(long i=0;i<(long)M*K;++i) hA[i]=(signed char)(((i*7+3)%251)-125);
1755    for(long i=0;i<(long)K*N;++i) hB[i]=(signed char)(((i*5+1)%251)-125);
1756    signed char *dA,*dB; int *dC,*hC=(int*)malloc((size_t)M*N*4);
1757    cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);
1758    cudaMemcpy(dA,hA,(size_t)M*K,cudaMemcpyHostToDevice);cudaMemcpy(dB,hB,(size_t)K*N,cudaMemcpyHostToDevice);
1759    dim3 grid(N/64,M/128); gemm_mma_tn_cpasync<<<grid,128>>>(dA,dB,dC,M,N,K); cudaDeviceSynchronize();
1760    cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
1761    for(long i=0;i<(long)M*N;++i) printf("%d\n",hC[i]); return 0; }}
1762"#,
1763        kernel = mma_tn_cpasync_kernel(),
1764        m = m,
1765        k = k,
1766        n = n
1767    )
1768}
1769
1770pub fn emit_mma_tn_cpasync_bench(m: usize, k: usize, n: usize) -> String {
1771    format!(
1772        r#"{kernel}
1773#include <cstdio>
1774#include <cstdlib>
1775int main() {{ const int M={m},K={k},N={n};
1776    signed char *dA,*dB; int *dC;
1777    cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);
1778    cudaMemset(dA,1,(size_t)M*K);cudaMemset(dB,1,(size_t)K*N);
1779    dim3 grid(N/64,M/128);
1780    gemm_mma_tn_cpasync<<<grid,128>>>(dA,dB,dC,M,N,K);
1781    cudaError_t err=cudaDeviceSynchronize(); if(err){{printf("LAUNCH_ERROR %s\n",cudaGetErrorString(err));return 1;}}
1782    int *hC=(int*)malloc((size_t)M*N*4); cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
1783    long step=((long)M*N)/4096+1; for(long i=0;i<(long)M*N;i+=step) if(hC[i]!=K){{printf("INCORRECT at %ld: %d!=%d\n",i,hC[i],K);return 1;}}
1784    cudaEvent_t s,e; cudaEventCreate(&s); cudaEventCreate(&e); int IT=30; float total=0;
1785    for(int it=0;it<IT;it++){{ cudaEventRecord(s); gemm_mma_tn_cpasync<<<grid,128>>>(dA,dB,dC,M,N,K); cudaEventRecord(e); cudaEventSynchronize(e); float ms=0; cudaEventElapsedTime(&ms,s,e); total+=ms; }}
1786    printf("GOPS %.1f\n", 2.0*M*N*K/((total/IT)/1e3)/1e9); return 0; }}
1787"#,
1788        kernel = mma_tn_cpasync_kernel(),
1789        m = m,
1790        k = k,
1791        n = n
1792    )
1793}
1794
1795// ─────────────────────── rung 7: col-major-B TN mma.sync.s8 — cuBLAS's OWN data layout ────────────────────
1796// The last starvation was the B fragment load: byte-strided (stride BN) because B was row-major. cuBLAS is
1797// fast in TN precisely because B arrives COL-MAJOR (K contiguous), so the tensor-core fragment load is one
1798// contiguous int32. Our COMPILER, choosing the Tn layout, emits exactly this: A row-major (contiguous k), B
1799// col-major (contiguous k) — both operands K-contiguous, the mma.sync.s8 native feed. cp.async double-buffered.
1800// This is the compiler emitting cuBLAS's kernel shape. Block 128x64, BK=32, 2x2 warps.
1801fn mma_tn_colb_kernel() -> &'static str {
1802    r#"#include <cuda_pipeline.h>
1803#define BM 128
1804#define BN 64
1805#define BK 32
1806#define WARPS_M 2
1807#define WARPS_N 2
1808#define WM (BM / WARPS_M)
1809#define WN (BN / WARPS_N)
1810#define NTHREAD (WARPS_M * WARPS_N * 32)
1811#define PK4(p,st) (((p)[0]&0xff)|(((p)[st]&0xff)<<8)|(((p)[2*(st)]&0xff)<<16)|(((p)[3*(st)]&0xff)<<24))
1812// C(MxN) = A(MxK, row-major) . B(KxN), with B stored COL-MAJOR: Bcol[n*K + k]. Both operands K-contiguous.
1813extern "C" __global__ void gemm_mma_tn_colb(const signed char* A, const signed char* Bcol, int* C, int M, int N, int K) {
1814    __shared__ signed char As[2][BM * BK];   // [m][k], k contiguous
1815    __shared__ signed char Bs[2][BN * BK];   // [n][k], k contiguous  (col-major tile)
1816    const int tid = threadIdx.x, warpId = tid / 32, lane = tid % 32;
1817    const int gid = lane / 4, tg = lane % 4;
1818    const int warp_row = (warpId / WARPS_N) * WM, warp_col = (warpId % WARPS_N) * WN;
1819    const int block_row = blockIdx.y * BM, block_col = blockIdx.x * BN;
1820    const int nstages = K / BK;
1821
1822    int c[WM / 16][WN / 8][4];
1823    #pragma unroll
1824    for (int mi = 0; mi < WM / 16; ++mi)
1825        #pragma unroll
1826        for (int ni = 0; ni < WN / 8; ++ni) { c[mi][ni][0]=c[mi][ni][1]=c[mi][ni][2]=c[mi][ni][3]=0; }
1827
1828    #define LOAD_STAGE(buf, st) do { int _k0 = (st) * BK; \
1829        for (int idx = tid; idx < (BM * BK) / 16; idx += NTHREAD) { int r=(idx*16)/BK, cc=(idx*16)%BK; \
1830            __pipeline_memcpy_async(&As[buf][r*BK+cc], &A[(block_row+r)*K + _k0+cc], 16); } \
1831        for (int idx = tid; idx < (BN * BK) / 16; idx += NTHREAD) { int r=(idx*16)/BK, cc=(idx*16)%BK; \
1832            __pipeline_memcpy_async(&Bs[buf][r*BK+cc], &Bcol[(block_col+r)*K + _k0+cc], 16); } \
1833        __pipeline_commit(); } while (0)
1834
1835    LOAD_STAGE(0, 0);
1836    for (int s = 0; s < nstages; ++s) {
1837        if (s + 1 < nstages) LOAD_STAGE((s + 1) & 1, s + 1);
1838        __pipeline_wait_prior(s + 1 < nstages ? 1 : 0);
1839        __syncthreads();
1840        int buf = s & 1;
1841        #pragma unroll
1842        for (int kk = 0; kk < BK; kk += 16) {
1843            #pragma unroll
1844            for (int mi = 0; mi < WM / 16; ++mi) {
1845                signed char* Ap0 = &As[buf][(warp_row + mi*16 + gid) * BK + kk + tg*4];
1846                signed char* Ap1 = &As[buf][(warp_row + mi*16 + gid + 8) * BK + kk + tg*4];
1847                int a0 = PK4(Ap0, 1), a1 = PK4(Ap1, 1);
1848                #pragma unroll
1849                for (int ni = 0; ni < WN / 8; ++ni) {
1850                    // B fragment: col=warp_col+ni*8+gid, k=kk+tg*4..+3 — now CONTIGUOUS (stride 1).
1851                    signed char* Bp = &Bs[buf][(warp_col + ni*8 + gid) * BK + kk + tg*4];
1852                    int b0 = PK4(Bp, 1);
1853                    asm volatile("mma.sync.aligned.m16n8k16.row.col.s32.s8.s8.s32 {%0,%1,%2,%3},{%4,%5},{%6},{%0,%1,%2,%3};"
1854                        : "+r"(c[mi][ni][0]),"+r"(c[mi][ni][1]),"+r"(c[mi][ni][2]),"+r"(c[mi][ni][3])
1855                        : "r"(a0),"r"(a1),"r"(b0));
1856                }
1857            }
1858        }
1859        __syncthreads();
1860    }
1861    #pragma unroll
1862    for (int mi = 0; mi < WM / 16; ++mi)
1863        #pragma unroll
1864        for (int ni = 0; ni < WN / 8; ++ni) {
1865            int rb = block_row + warp_row + mi*16, cb = block_col + warp_col + ni*8;
1866            C[(rb+gid)*N + cb+tg*2]     = c[mi][ni][0];
1867            C[(rb+gid)*N + cb+tg*2+1]   = c[mi][ni][1];
1868            C[(rb+gid+8)*N + cb+tg*2]   = c[mi][ni][2];
1869            C[(rb+gid+8)*N + cb+tg*2+1] = c[mi][ni][3];
1870        }
1871}
1872"#
1873}
1874
1875pub fn emit_mma_tn_colb_program(m: usize, k: usize, n: usize) -> String {
1876    format!(
1877        r#"{kernel}
1878#include <cstdio>
1879#include <cstdlib>
1880int main() {{ const int M={m},K={k},N={n};
1881    signed char *hA=(signed char*)malloc((size_t)M*K),*hB=(signed char*)malloc((size_t)K*N);
1882    for(long i=0;i<(long)M*K;++i) hA[i]=(signed char)(((i*7+3)%251)-125);
1883    // B stored COL-MAJOR: hB[n*K + k] holds B[k][n]; fill so oracle C[i][j]=sum_k A[i][k]*B[k][j].
1884    for(long n=0;n<N;++n) for(long kk=0;kk<K;++kk){{ long bkn=(long)kk*N+n; hB[n*K+kk]=(signed char)(((bkn*5+1)%251)-125); }}
1885    signed char *dA,*dB; int *dC,*hC=(int*)malloc((size_t)M*N*4);
1886    cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);
1887    cudaMemcpy(dA,hA,(size_t)M*K,cudaMemcpyHostToDevice);cudaMemcpy(dB,hB,(size_t)K*N,cudaMemcpyHostToDevice);
1888    dim3 grid(N/64,M/128); gemm_mma_tn_colb<<<grid,128>>>(dA,dB,dC,M,N,K); cudaDeviceSynchronize();
1889    cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
1890    for(long i=0;i<(long)M*N;++i) printf("%d\n",hC[i]); return 0; }}
1891"#,
1892        kernel = mma_tn_colb_kernel(),
1893        m = m,
1894        k = k,
1895        n = n
1896    )
1897}
1898
1899pub fn emit_mma_tn_colb_bench(m: usize, k: usize, n: usize) -> String {
1900    format!(
1901        r#"{kernel}
1902#include <cstdio>
1903#include <cstdlib>
1904int main() {{ const int M={m},K={k},N={n};
1905    signed char *dA,*dB; int *dC;
1906    cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);
1907    cudaMemset(dA,1,(size_t)M*K);cudaMemset(dB,1,(size_t)K*N);
1908    dim3 grid(N/64,M/128);
1909    gemm_mma_tn_colb<<<grid,128>>>(dA,dB,dC,M,N,K);
1910    cudaError_t err=cudaDeviceSynchronize(); if(err){{printf("LAUNCH_ERROR %s\n",cudaGetErrorString(err));return 1;}}
1911    int *hC=(int*)malloc((size_t)M*N*4); cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
1912    long step=((long)M*N)/4096+1; for(long i=0;i<(long)M*N;i+=step) if(hC[i]!=K){{printf("INCORRECT at %ld: %d!=%d\n",i,hC[i],K);return 1;}}
1913    cudaEvent_t s,e; cudaEventCreate(&s); cudaEventCreate(&e); int IT=30; float total=0;
1914    for(int it=0;it<IT;it++){{ cudaEventRecord(s); gemm_mma_tn_colb<<<grid,128>>>(dA,dB,dC,M,N,K); cudaEventRecord(e); cudaEventSynchronize(e); float ms=0; cudaEventElapsedTime(&ms,s,e); total+=ms; }}
1915    printf("GOPS %.1f\n", 2.0*M*N*K/((total/IT)/1e3)/1e9); return 0; }}
1916"#,
1917        kernel = mma_tn_colb_kernel(),
1918        m = m,
1919        k = k,
1920        n = n
1921    )
1922}
1923
1924// ─────────────────────── rung 8: ldmatrix + mma.sync.s8 TN — the EARNED cuBLAS-class fragment feed ─────────
1925// The manual per-thread fragment loads were the wall. `ldmatrix.sync` loads a whole warp's mma fragment
1926// cooperatively in ONE instruction, in the exact register layout mma.sync wants — this is how cuBLAS feeds
1927// the tensor cores. A (row-major) via ldmatrix.x2 → {a0,a1}; B (col-major, K-contiguous) via ldmatrix.x1 →
1928// {b0}. The ldmatrix output distribution provably equals the fragment map we already verified by hand. Both
1929// operands K-contiguous (cuBLAS's TN layout), cp.async double-buffered. This is the earned cuBLAS-class kernel.
1930fn mma_tn_ldmatrix_kernel() -> &'static str {
1931    r#"#include <cuda_pipeline.h>
1932#define BM 128
1933#define BN 64
1934#define BK 32
1935#define WARPS_M 2
1936#define WARPS_N 2
1937#define WM (BM / WARPS_M)
1938#define WN (BN / WARPS_N)
1939#define NTHREAD (WARPS_M * WARPS_N * 32)
1940// C(MxN)=A(MxK,row-major).B(KxN, stored COL-MAJOR: Bcol[n*K+k]). ldmatrix feeds mma.sync.aligned.m16n8k16.s8.
1941extern "C" __global__ void gemm_mma_tn_ldm(const signed char* A, const signed char* Bcol, int* C, int M, int N, int K) {
1942    __shared__ signed char As[2][BM * BK];   // [m][k]
1943    __shared__ signed char Bs[2][BN * BK];   // [n][k]  (col-major tile)
1944    const int tid = threadIdx.x, warpId = tid / 32, lane = tid % 32;
1945    const int gid = lane / 4, tg = lane % 4;
1946    const int warp_row = (warpId / WARPS_N) * WM, warp_col = (warpId % WARPS_N) * WN;
1947    const int block_row = blockIdx.y * BM, block_col = blockIdx.x * BN;
1948    const int nstages = K / BK;
1949
1950    int c[WM / 16][WN / 8][4];
1951    #pragma unroll
1952    for (int mi = 0; mi < WM / 16; ++mi)
1953        #pragma unroll
1954        for (int ni = 0; ni < WN / 8; ++ni) { c[mi][ni][0]=c[mi][ni][1]=c[mi][ni][2]=c[mi][ni][3]=0; }
1955
1956    #define LOAD_STAGE(buf, st) do { int _k0 = (st) * BK; \
1957        for (int idx = tid; idx < (BM * BK) / 16; idx += NTHREAD) { int r=(idx*16)/BK, cc=(idx*16)%BK; \
1958            __pipeline_memcpy_async(&As[buf][r*BK+cc], &A[(block_row+r)*K + _k0+cc], 16); } \
1959        for (int idx = tid; idx < (BN * BK) / 16; idx += NTHREAD) { int r=(idx*16)/BK, cc=(idx*16)%BK; \
1960            __pipeline_memcpy_async(&Bs[buf][r*BK+cc], &Bcol[(block_col+r)*K + _k0+cc], 16); } \
1961        __pipeline_commit(); } while (0)
1962
1963    LOAD_STAGE(0, 0);
1964    for (int s = 0; s < nstages; ++s) {
1965        if (s + 1 < nstages) LOAD_STAGE((s + 1) & 1, s + 1);
1966        __pipeline_wait_prior(s + 1 < nstages ? 1 : 0);
1967        __syncthreads();
1968        int buf = s & 1;
1969        #pragma unroll
1970        for (int kk = 0; kk < BK; kk += 16) {
1971            int a[WM / 16][2], b[WN / 8];
1972            #pragma unroll
1973            for (int mi = 0; mi < WM / 16; ++mi) {
1974                unsigned aAddr = (unsigned)__cvta_generic_to_shared(&As[buf][(warp_row + mi*16 + (lane & 15)) * BK + kk]);
1975                asm volatile("ldmatrix.sync.aligned.m8n8.x2.shared.b16 {%0,%1},[%2];" : "=r"(a[mi][0]), "=r"(a[mi][1]) : "r"(aAddr));
1976            }
1977            #pragma unroll
1978            for (int ni = 0; ni < WN / 8; ++ni) {
1979                unsigned bAddr = (unsigned)__cvta_generic_to_shared(&Bs[buf][(warp_col + ni*8 + (lane & 7)) * BK + kk]);
1980                asm volatile("ldmatrix.sync.aligned.m8n8.x1.shared.b16 {%0},[%1];" : "=r"(b[ni]) : "r"(bAddr));
1981            }
1982            #pragma unroll
1983            for (int mi = 0; mi < WM / 16; ++mi)
1984                #pragma unroll
1985                for (int ni = 0; ni < WN / 8; ++ni)
1986                    asm volatile("mma.sync.aligned.m16n8k16.row.col.s32.s8.s8.s32 {%0,%1,%2,%3},{%4,%5},{%6},{%0,%1,%2,%3};"
1987                        : "+r"(c[mi][ni][0]),"+r"(c[mi][ni][1]),"+r"(c[mi][ni][2]),"+r"(c[mi][ni][3])
1988                        : "r"(a[mi][0]),"r"(a[mi][1]),"r"(b[ni]));
1989        }
1990        __syncthreads();
1991    }
1992    #pragma unroll
1993    for (int mi = 0; mi < WM / 16; ++mi)
1994        #pragma unroll
1995        for (int ni = 0; ni < WN / 8; ++ni) {
1996            int rb = block_row + warp_row + mi*16, cb = block_col + warp_col + ni*8;
1997            C[(rb+gid)*N + cb+tg*2]     = c[mi][ni][0];
1998            C[(rb+gid)*N + cb+tg*2+1]   = c[mi][ni][1];
1999            C[(rb+gid+8)*N + cb+tg*2]   = c[mi][ni][2];
2000            C[(rb+gid+8)*N + cb+tg*2+1] = c[mi][ni][3];
2001        }
2002}
2003"#
2004}
2005
2006pub fn emit_mma_tn_ldmatrix_program(m: usize, k: usize, n: usize) -> String {
2007    format!(
2008        r#"{kernel}
2009#include <cstdio>
2010#include <cstdlib>
2011int main() {{ const int M={m},K={k},N={n};
2012    signed char *hA=(signed char*)malloc((size_t)M*K),*hB=(signed char*)malloc((size_t)K*N);
2013    for(long i=0;i<(long)M*K;++i) hA[i]=(signed char)(((i*7+3)%251)-125);
2014    for(long n=0;n<N;++n) for(long kk=0;kk<K;++kk){{ long bkn=(long)kk*N+n; hB[n*K+kk]=(signed char)(((bkn*5+1)%251)-125); }}
2015    signed char *dA,*dB; int *dC,*hC=(int*)malloc((size_t)M*N*4);
2016    cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);
2017    cudaMemcpy(dA,hA,(size_t)M*K,cudaMemcpyHostToDevice);cudaMemcpy(dB,hB,(size_t)K*N,cudaMemcpyHostToDevice);
2018    dim3 grid(N/64,M/128); gemm_mma_tn_ldm<<<grid,128>>>(dA,dB,dC,M,N,K);
2019    cudaError_t e=cudaDeviceSynchronize(); if(e){{printf("ERR %s\n",cudaGetErrorString(e));return 1;}}
2020    cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
2021    for(long i=0;i<(long)M*N;++i) printf("%d\n",hC[i]); return 0; }}
2022"#,
2023        kernel = mma_tn_ldmatrix_kernel(),
2024        m = m,
2025        k = k,
2026        n = n
2027    )
2028}
2029
2030pub fn emit_mma_tn_ldmatrix_bench(m: usize, k: usize, n: usize) -> String {
2031    format!(
2032        r#"{kernel}
2033#include <cstdio>
2034#include <cstdlib>
2035int main() {{ const int M={m},K={k},N={n};
2036    signed char *dA,*dB; int *dC;
2037    cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);
2038    cudaMemset(dA,1,(size_t)M*K);cudaMemset(dB,1,(size_t)K*N);
2039    dim3 grid(N/64,M/128);
2040    gemm_mma_tn_ldm<<<grid,128>>>(dA,dB,dC,M,N,K);
2041    cudaError_t err=cudaDeviceSynchronize(); if(err){{printf("LAUNCH_ERROR %s\n",cudaGetErrorString(err));return 1;}}
2042    int *hC=(int*)malloc((size_t)M*N*4); cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
2043    long step=((long)M*N)/4096+1; for(long i=0;i<(long)M*N;i+=step) if(hC[i]!=K){{printf("INCORRECT at %ld: %d!=%d\n",i,hC[i],K);return 1;}}
2044    cudaEvent_t s,e; cudaEventCreate(&s); cudaEventCreate(&e); int IT=30; float total=0;
2045    for(int it=0;it<IT;it++){{ cudaEventRecord(s); gemm_mma_tn_ldm<<<grid,128>>>(dA,dB,dC,M,N,K); cudaEventRecord(e); cudaEventSynchronize(e); float ms=0; cudaEventElapsedTime(&ms,s,e); total+=ms; }}
2046    printf("GOPS %.1f\n", 2.0*M*N*K/((total/IT)/1e3)/1e9); return 0; }}
2047"#,
2048        kernel = mma_tn_ldmatrix_kernel(),
2049        m = m,
2050        k = k,
2051        n = n
2052    )
2053}
2054
2055// ─────────────────────── rung 8b: parameterized ldmatrix kernel (config sweep toward cuBLAS-TN) ────────────
2056/// Emit the ldmatrix+mma.sync.s8 TN kernel for a specific config so the frontier can sweep block/warp/stage
2057/// shapes on-device. `stages` shared buffers (cp.async N-stage). WN must be a multiple of 8, WM of 16.
2058pub fn emit_mma_tn_ldm_cfg_bench(
2059    bm: usize,
2060    bn: usize,
2061    bk: usize,
2062    warps_m: usize,
2063    warps_n: usize,
2064    stages: usize,
2065    m: usize,
2066    k: usize,
2067    n: usize,
2068) -> String {
2069    let defs = format!(
2070        "#define BM {bm}\n#define BN {bn}\n#define BK {bk}\n#define WARPS_M {warps_m}\n#define WARPS_N {warps_n}\n#define STAGES {stages}\n"
2071    );
2072    let kernel = format!("{defs}{}", MMA_TN_LDM_CFG_BODY);
2073    format!(
2074        r#"{kernel}
2075#include <cstdio>
2076#include <cstdlib>
2077int main() {{ const int M={m},K={k},N={n};
2078    signed char *dA,*dB; int *dC;
2079    cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);
2080    cudaMemset(dA,1,(size_t)M*K);cudaMemset(dB,1,(size_t)K*N);
2081    dim3 grid(N/{bn}, M/{bm});
2082    gemm_mma_tn_ldm<<<grid,{threads}>>>(dA,dB,dC,M,N,K);
2083    cudaError_t err=cudaDeviceSynchronize(); if(err){{printf("LAUNCH_ERROR %s\n",cudaGetErrorString(err));return 1;}}
2084    int *hC=(int*)malloc((size_t)M*N*4); cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
2085    long step=((long)M*N)/4096+1; for(long i=0;i<(long)M*N;i+=step) if(hC[i]!=K){{printf("INCORRECT %ld: %d!=%d\n",i,hC[i],K);return 1;}}
2086    cudaEvent_t s,e; cudaEventCreate(&s); cudaEventCreate(&e); int IT=30; float total=0;
2087    for(int it=0;it<IT;it++){{ cudaEventRecord(s); gemm_mma_tn_ldm<<<grid,{threads}>>>(dA,dB,dC,M,N,K); cudaEventRecord(e); cudaEventSynchronize(e); float ms=0; cudaEventElapsedTime(&ms,s,e); total+=ms; }}
2088    printf("GOPS %.1f\n", 2.0*M*N*K/((total/IT)/1e3)/1e9); return 0; }}
2089"#,
2090        kernel = kernel,
2091        m = m,
2092        k = k,
2093        n = n,
2094        bm = bm,
2095        bn = bn,
2096        threads = warps_m * warps_n * 32
2097    )
2098}
2099
2100const MMA_TN_LDM_CFG_BODY: &str = r#"#include <cuda_pipeline.h>
2101#define WM (BM / WARPS_M)
2102#define WN (BN / WARPS_N)
2103#define NTHREAD (WARPS_M * WARPS_N * 32)
2104extern "C" __global__ void gemm_mma_tn_ldm(const signed char* A, const signed char* Bcol, int* C, int M, int N, int K) {
2105    __shared__ signed char As[STAGES][BM * BK];
2106    __shared__ signed char Bs[STAGES][BN * BK];
2107    const int tid = threadIdx.x, warpId = tid / 32, lane = tid % 32;
2108    const int gid = lane / 4, tg = lane % 4;
2109    const int warp_row = (warpId / WARPS_N) * WM, warp_col = (warpId % WARPS_N) * WN;
2110    const int block_row = blockIdx.y * BM, block_col = blockIdx.x * BN;
2111    const int nstages = K / BK;
2112    int c[WM / 16][WN / 8][4];
2113    #pragma unroll
2114    for (int mi=0; mi<WM/16; ++mi)
2115      #pragma unroll
2116      for (int ni=0; ni<WN/8; ++ni) { c[mi][ni][0]=c[mi][ni][1]=c[mi][ni][2]=c[mi][ni][3]=0; }
2117    #define LOAD_STAGE(buf, st) do { int _k0=(st)*BK; \
2118        for (int idx=tid; idx<(BM*BK)/16; idx+=NTHREAD){ int r=(idx*16)/BK, cc=(idx*16)%BK; \
2119            __pipeline_memcpy_async(&As[buf][r*BK+cc], &A[(block_row+r)*K + _k0+cc], 16); } \
2120        for (int idx=tid; idx<(BN*BK)/16; idx+=NTHREAD){ int r=(idx*16)/BK, cc=(idx*16)%BK; \
2121            __pipeline_memcpy_async(&Bs[buf][r*BK+cc], &Bcol[(block_col+r)*K + _k0+cc], 16); } \
2122        __pipeline_commit(); } while(0)
2123    #pragma unroll
2124    for (int i=0;i<STAGES-1;++i){ if(i<nstages) LOAD_STAGE(i,i); else __pipeline_commit(); }
2125    for (int s=0; s<nstages; ++s) {
2126        __pipeline_wait_prior(STAGES-2);
2127        __syncthreads();
2128        int buf = s % STAGES;
2129        #pragma unroll
2130        for (int kk=0; kk<BK; kk+=16) {
2131            int a[WM/16][2], b[WN/8];
2132            #pragma unroll
2133            for (int mi=0; mi<WM/16; ++mi){
2134                unsigned aA=(unsigned)__cvta_generic_to_shared(&As[buf][(warp_row+mi*16+(lane&15))*BK+kk]);
2135                asm volatile("ldmatrix.sync.aligned.m8n8.x2.shared.b16 {%0,%1},[%2];":"=r"(a[mi][0]),"=r"(a[mi][1]):"r"(aA)); }
2136            #pragma unroll
2137            for (int ni=0; ni<WN/8; ++ni){
2138                unsigned bA=(unsigned)__cvta_generic_to_shared(&Bs[buf][(warp_col+ni*8+(lane&7))*BK+kk]);
2139                asm volatile("ldmatrix.sync.aligned.m8n8.x1.shared.b16 {%0},[%1];":"=r"(b[ni]):"r"(bA)); }
2140            #pragma unroll
2141            for (int mi=0; mi<WM/16; ++mi)
2142              #pragma unroll
2143              for (int ni=0; ni<WN/8; ++ni)
2144                asm volatile("mma.sync.aligned.m16n8k16.row.col.s32.s8.s8.s32 {%0,%1,%2,%3},{%4,%5},{%6},{%0,%1,%2,%3};"
2145                  :"+r"(c[mi][ni][0]),"+r"(c[mi][ni][1]),"+r"(c[mi][ni][2]),"+r"(c[mi][ni][3])
2146                  :"r"(a[mi][0]),"r"(a[mi][1]),"r"(b[ni]));
2147        }
2148        __syncthreads();
2149        int nxt=s+STAGES-1; if(nxt<nstages) LOAD_STAGE(nxt%STAGES, nxt); else __pipeline_commit();
2150    }
2151    #pragma unroll
2152    for (int mi=0; mi<WM/16; ++mi)
2153      #pragma unroll
2154      for (int ni=0; ni<WN/8; ++ni) {
2155        int rb=block_row+warp_row+mi*16, cb=block_col+warp_col+ni*8;
2156        C[(rb+gid)*N+cb+tg*2]=c[mi][ni][0]; C[(rb+gid)*N+cb+tg*2+1]=c[mi][ni][1];
2157        C[(rb+gid+8)*N+cb+tg*2]=c[mi][ni][2]; C[(rb+gid+8)*N+cb+tg*2+1]=c[mi][ni][3];
2158      }
2159}
2160"#;
2161
2162// ─────────────────────── rung 9: SWIZZLED ldmatrix TN — the bank-conflict-free tensor-core feed ────────────
2163// ldmatrix has each of 8 threads read a 16-byte row; with row stride BK=32 bytes, rows 0&4, 1&5, 2&6, 3&7 hit
2164// the same 32-bank set (2-way conflict) — the last thing serializing the tensor-core feed. The swizzle is pure
2165// math: give each shared row a stride SBK = BK + PAD so 8 consecutive rows land in distinct bank-sets. With
2166// PAD=16 (SBK=48), consecutive rows differ by 48B = 12 banks: rows 0..7 -> banks 0,12,24,4,16,28,8,20, all
2167// distinct -> conflict-free ldmatrix. cp.async writes the padded layout; ldmatrix reads it. Same result, no
2168// conflicts. This is the earned cuBLAS-class feed.
2169pub fn emit_mma_tn_ldm_swizzle_bench(
2170    bm: usize,
2171    bn: usize,
2172    bk: usize,
2173    warps_m: usize,
2174    warps_n: usize,
2175    stages: usize,
2176    pad: usize,
2177    m: usize,
2178    k: usize,
2179    n: usize,
2180) -> String {
2181    let defs = format!(
2182        "#define BM {bm}\n#define BN {bn}\n#define BK {bk}\n#define WARPS_M {warps_m}\n#define WARPS_N {warps_n}\n#define STAGES {stages}\n#define PAD {pad}\n"
2183    );
2184    let kernel = format!("{defs}{}", MMA_TN_LDM_SWZ_BODY);
2185    format!(
2186        r#"{kernel}
2187#include <cstdio>
2188#include <cstdlib>
2189int main() {{ const int M={m},K={k},N={n};
2190    signed char *dA,*dB; int *dC;
2191    cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);
2192    cudaMemset(dA,1,(size_t)M*K);cudaMemset(dB,1,(size_t)K*N);
2193    dim3 grid(N/{bn}, M/{bm});
2194    gemm_mma_tn_ldm<<<grid,{threads}>>>(dA,dB,dC,M,N,K);
2195    cudaError_t err=cudaDeviceSynchronize(); if(err){{printf("LAUNCH_ERROR %s\n",cudaGetErrorString(err));return 1;}}
2196    int *hC=(int*)malloc((size_t)M*N*4); cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
2197    long step=((long)M*N)/4096+1; for(long i=0;i<(long)M*N;i+=step) if(hC[i]!=K){{printf("INCORRECT %ld: %d!=%d\n",i,hC[i],K);return 1;}}
2198    cudaEvent_t s,e; cudaEventCreate(&s); cudaEventCreate(&e); int IT=30; float total=0;
2199    for(int it=0;it<IT;it++){{ cudaEventRecord(s); gemm_mma_tn_ldm<<<grid,{threads}>>>(dA,dB,dC,M,N,K); cudaEventRecord(e); cudaEventSynchronize(e); float ms=0; cudaEventElapsedTime(&ms,s,e); total+=ms; }}
2200    printf("GOPS %.1f\n", 2.0*M*N*K/((total/IT)/1e3)/1e9); return 0; }}
2201"#,
2202        kernel = kernel,
2203        m = m,
2204        k = k,
2205        n = n,
2206        bm = bm,
2207        bn = bn,
2208        threads = warps_m * warps_n * 32
2209    )
2210}
2211
2212/// The swizzled program (varied-fill oracle correctness).
2213pub fn emit_mma_tn_ldm_swizzle_program(
2214    bm: usize,
2215    bn: usize,
2216    bk: usize,
2217    warps_m: usize,
2218    warps_n: usize,
2219    stages: usize,
2220    pad: usize,
2221    m: usize,
2222    k: usize,
2223    n: usize,
2224) -> String {
2225    let defs = format!(
2226        "#define BM {bm}\n#define BN {bn}\n#define BK {bk}\n#define WARPS_M {warps_m}\n#define WARPS_N {warps_n}\n#define STAGES {stages}\n#define PAD {pad}\n"
2227    );
2228    let kernel = format!("{defs}{}", MMA_TN_LDM_SWZ_BODY);
2229    format!(
2230        r#"{kernel}
2231#include <cstdio>
2232#include <cstdlib>
2233int main() {{ const int M={m},K={k},N={n};
2234    signed char *hA=(signed char*)malloc((size_t)M*K),*hB=(signed char*)malloc((size_t)K*N);
2235    for(long i=0;i<(long)M*K;++i) hA[i]=(signed char)(((i*7+3)%251)-125);
2236    for(long nn=0;nn<N;++nn) for(long kk=0;kk<K;++kk){{ long bkn=(long)kk*N+nn; hB[nn*K+kk]=(signed char)(((bkn*5+1)%251)-125); }}
2237    signed char *dA,*dB; int *dC,*hC=(int*)malloc((size_t)M*N*4);
2238    cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);
2239    cudaMemcpy(dA,hA,(size_t)M*K,cudaMemcpyHostToDevice);cudaMemcpy(dB,hB,(size_t)K*N,cudaMemcpyHostToDevice);
2240    dim3 grid(N/{bn},M/{bm}); gemm_mma_tn_ldm<<<grid,{threads}>>>(dA,dB,dC,M,N,K);
2241    cudaError_t e=cudaDeviceSynchronize(); if(e){{printf("ERR %s\n",cudaGetErrorString(e));return 1;}}
2242    cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
2243    for(long i=0;i<(long)M*N;++i) printf("%d\n",hC[i]); return 0; }}
2244"#,
2245        kernel = kernel,
2246        m = m,
2247        k = k,
2248        n = n,
2249        bm = bm,
2250        bn = bn,
2251        threads = warps_m * warps_n * 32
2252    )
2253}
2254
2255const MMA_TN_LDM_SWZ_BODY: &str = r#"#include <cuda_pipeline.h>
2256#define WM (BM / WARPS_M)
2257#define WN (BN / WARPS_N)
2258#define NTHREAD (WARPS_M * WARPS_N * 32)
2259#define SBK (BK + PAD)
2260extern "C" __global__ void gemm_mma_tn_ldm(const signed char* A, const signed char* Bcol, int* C, int M, int N, int K) {
2261    __shared__ signed char As[STAGES][BM * SBK];
2262    __shared__ signed char Bs[STAGES][BN * SBK];
2263    const int tid = threadIdx.x, warpId = tid / 32, lane = tid % 32;
2264    const int gid = lane / 4, tg = lane % 4;
2265    const int warp_row = (warpId / WARPS_N) * WM, warp_col = (warpId % WARPS_N) * WN;
2266    const int block_row = blockIdx.y * BM, block_col = blockIdx.x * BN;
2267    const int nstages = K / BK;
2268    int c[WM / 16][WN / 8][4];
2269    #pragma unroll
2270    for (int mi=0; mi<WM/16; ++mi)
2271      #pragma unroll
2272      for (int ni=0; ni<WN/8; ++ni) { c[mi][ni][0]=c[mi][ni][1]=c[mi][ni][2]=c[mi][ni][3]=0; }
2273    #define LOAD_STAGE(buf, st) do { int _k0=(st)*BK; \
2274        for (int idx=tid; idx<(BM*BK)/16; idx+=NTHREAD){ int r=(idx*16)/BK, cc=(idx*16)%BK; \
2275            __pipeline_memcpy_async(&As[buf][r*SBK+cc], &A[(block_row+r)*K + _k0+cc], 16); } \
2276        for (int idx=tid; idx<(BN*BK)/16; idx+=NTHREAD){ int r=(idx*16)/BK, cc=(idx*16)%BK; \
2277            __pipeline_memcpy_async(&Bs[buf][r*SBK+cc], &Bcol[(block_col+r)*K + _k0+cc], 16); } \
2278        __pipeline_commit(); } while(0)
2279    #pragma unroll
2280    for (int i=0;i<STAGES-1;++i){ if(i<nstages) LOAD_STAGE(i,i); else __pipeline_commit(); }
2281    for (int s=0; s<nstages; ++s) {
2282        __pipeline_wait_prior(STAGES-2);
2283        __syncthreads();
2284        int buf = s % STAGES;
2285        #pragma unroll
2286        for (int kk=0; kk<BK; kk+=16) {
2287            int a[WM/16][2], b[WN/8];
2288            #pragma unroll
2289            for (int mi=0; mi<WM/16; ++mi){
2290                unsigned aA=(unsigned)__cvta_generic_to_shared(&As[buf][(warp_row+mi*16+(lane&15))*SBK+kk]);
2291                asm volatile("ldmatrix.sync.aligned.m8n8.x2.shared.b16 {%0,%1},[%2];":"=r"(a[mi][0]),"=r"(a[mi][1]):"r"(aA)); }
2292            #pragma unroll
2293            for (int ni=0; ni<WN/8; ++ni){
2294                unsigned bA=(unsigned)__cvta_generic_to_shared(&Bs[buf][(warp_col+ni*8+(lane&7))*SBK+kk]);
2295                asm volatile("ldmatrix.sync.aligned.m8n8.x1.shared.b16 {%0},[%1];":"=r"(b[ni]):"r"(bA)); }
2296            #pragma unroll
2297            for (int mi=0; mi<WM/16; ++mi)
2298              #pragma unroll
2299              for (int ni=0; ni<WN/8; ++ni)
2300                asm volatile("mma.sync.aligned.m16n8k16.row.col.s32.s8.s8.s32 {%0,%1,%2,%3},{%4,%5},{%6},{%0,%1,%2,%3};"
2301                  :"+r"(c[mi][ni][0]),"+r"(c[mi][ni][1]),"+r"(c[mi][ni][2]),"+r"(c[mi][ni][3])
2302                  :"r"(a[mi][0]),"r"(a[mi][1]),"r"(b[ni]));
2303        }
2304        __syncthreads();
2305        int nxt=s+STAGES-1; if(nxt<nstages) LOAD_STAGE(nxt%STAGES, nxt); else __pipeline_commit();
2306    }
2307    #pragma unroll
2308    for (int mi=0; mi<WM/16; ++mi)
2309      #pragma unroll
2310      for (int ni=0; ni<WN/8; ++ni) {
2311        int rb=block_row+warp_row+mi*16, cb=block_col+warp_col+ni*8;
2312        C[(rb+gid)*N+cb+tg*2]=c[mi][ni][0]; C[(rb+gid)*N+cb+tg*2+1]=c[mi][ni][1];
2313        C[(rb+gid+8)*N+cb+tg*2]=c[mi][ni][2]; C[(rb+gid+8)*N+cb+tg*2+1]=c[mi][ni][3];
2314      }
2315}
2316"#;
2317
2318// ─────────────────────── rung 10: m16n8k32 mma.sync.s8 — 2x MACs/instruction (cuBLAS's mma shape) ──────────
2319// m16n8k16 issues one mma per 16 of K; m16n8k32 does 32 of K per instruction = HALF the mma + ldmatrix issue
2320// overhead for the same work. This is the int8 tensor-core op cuBLAS actually uses. A (16x32) via ldmatrix.x4
2321// -> {a0,a1,a2,a3}; col-major B (32x8) via ldmatrix.x2 -> {b0,b1}. Fragment map: a{0,2}=rows gid (k 0-3, 16-19),
2322// a{1,3}=rows gid+8; b0/b1 = k first/second 16-half. cp.async pipelined. Our OWN kernel, earned.
2323pub fn emit_mma_tn_k32_bench(
2324    bm: usize,
2325    bn: usize,
2326    warps_m: usize,
2327    warps_n: usize,
2328    stages: usize,
2329    m: usize,
2330    k: usize,
2331    n: usize,
2332) -> String {
2333    let defs = format!("#define BM {bm}\n#define BN {bn}\n#define BK 32\n#define WARPS_M {warps_m}\n#define WARPS_N {warps_n}\n#define STAGES {stages}\n");
2334    let kernel = format!("{defs}{}", MMA_TN_K32_BODY);
2335    format!(
2336        r#"{kernel}
2337#include <cstdio>
2338#include <cstdlib>
2339int main() {{ const int M={m},K={k},N={n};
2340    signed char *dA,*dB; int *dC;
2341    cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);
2342    cudaMemset(dA,1,(size_t)M*K);cudaMemset(dB,1,(size_t)K*N);
2343    dim3 grid(N/{bn}, M/{bm});
2344    gemm_mma_tn_k32<<<grid,{threads}>>>(dA,dB,dC,M,N,K);
2345    cudaError_t err=cudaDeviceSynchronize(); if(err){{printf("LAUNCH_ERROR %s\n",cudaGetErrorString(err));return 1;}}
2346    int *hC=(int*)malloc((size_t)M*N*4); cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
2347    long step=((long)M*N)/4096+1; for(long i=0;i<(long)M*N;i+=step) if(hC[i]!=K){{printf("INCORRECT %ld: %d!=%d\n",i,hC[i],K);return 1;}}
2348    cudaEvent_t s,e; cudaEventCreate(&s); cudaEventCreate(&e); int IT=30; float total=0;
2349    for(int it=0;it<IT;it++){{ cudaEventRecord(s); gemm_mma_tn_k32<<<grid,{threads}>>>(dA,dB,dC,M,N,K); cudaEventRecord(e); cudaEventSynchronize(e); float ms=0; cudaEventElapsedTime(&ms,s,e); total+=ms; }}
2350    printf("GOPS %.1f\n", 2.0*M*N*K/((total/IT)/1e3)/1e9); return 0; }}
2351"#,
2352        kernel = kernel,
2353        m = m,
2354        k = k,
2355        n = n,
2356        bm = bm,
2357        bn = bn,
2358        threads = warps_m * warps_n * 32
2359    )
2360}
2361
2362pub fn emit_mma_tn_k32_program(
2363    bm: usize,
2364    bn: usize,
2365    warps_m: usize,
2366    warps_n: usize,
2367    stages: usize,
2368    m: usize,
2369    k: usize,
2370    n: usize,
2371) -> String {
2372    let defs = format!("#define BM {bm}\n#define BN {bn}\n#define BK 32\n#define WARPS_M {warps_m}\n#define WARPS_N {warps_n}\n#define STAGES {stages}\n");
2373    let kernel = format!("{defs}{}", MMA_TN_K32_BODY);
2374    format!(
2375        r#"{kernel}
2376#include <cstdio>
2377#include <cstdlib>
2378int main() {{ const int M={m},K={k},N={n};
2379    signed char *hA=(signed char*)malloc((size_t)M*K),*hB=(signed char*)malloc((size_t)K*N);
2380    for(long i=0;i<(long)M*K;++i) hA[i]=(signed char)(((i*7+3)%251)-125);
2381    for(long nn=0;nn<N;++nn) for(long kk=0;kk<K;++kk){{ long bkn=(long)kk*N+nn; hB[nn*K+kk]=(signed char)(((bkn*5+1)%251)-125); }}
2382    signed char *dA,*dB; int *dC,*hC=(int*)malloc((size_t)M*N*4);
2383    cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);
2384    cudaMemcpy(dA,hA,(size_t)M*K,cudaMemcpyHostToDevice);cudaMemcpy(dB,hB,(size_t)K*N,cudaMemcpyHostToDevice);
2385    dim3 grid(N/{bn},M/{bm}); gemm_mma_tn_k32<<<grid,{threads}>>>(dA,dB,dC,M,N,K);
2386    cudaError_t e=cudaDeviceSynchronize(); if(e){{printf("ERR %s\n",cudaGetErrorString(e));return 1;}}
2387    cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
2388    for(long i=0;i<(long)M*N;++i) printf("%d\n",hC[i]); return 0; }}
2389"#,
2390        kernel = kernel,
2391        m = m,
2392        k = k,
2393        n = n,
2394        bm = bm,
2395        bn = bn,
2396        threads = warps_m * warps_n * 32
2397    )
2398}
2399
2400const MMA_TN_K32_BODY: &str = r#"#include <cuda_pipeline.h>
2401#define WM (BM / WARPS_M)
2402#define WN (BN / WARPS_N)
2403#define NTHREAD (WARPS_M * WARPS_N * 32)
2404extern "C" __global__ void gemm_mma_tn_k32(const signed char* A, const signed char* Bcol, int* C, int M, int N, int K) {
2405    __shared__ signed char As[STAGES][BM * BK];
2406    __shared__ signed char Bs[STAGES][BN * BK];
2407    const int tid = threadIdx.x, warpId = tid / 32, lane = tid % 32;
2408    const int gid = lane / 4, tg = lane % 4;
2409    const int warp_row = (warpId / WARPS_N) * WM, warp_col = (warpId % WARPS_N) * WN;
2410    const int block_row = blockIdx.y * BM, block_col = blockIdx.x * BN;
2411    const int nstages = K / BK;
2412    int c[WM / 16][WN / 8][4];
2413    #pragma unroll
2414    for (int mi=0; mi<WM/16; ++mi)
2415      #pragma unroll
2416      for (int ni=0; ni<WN/8; ++ni) { c[mi][ni][0]=c[mi][ni][1]=c[mi][ni][2]=c[mi][ni][3]=0; }
2417    #define LOAD_STAGE(buf, st) do { int _k0=(st)*BK; \
2418        for (int idx=tid; idx<(BM*BK)/16; idx+=NTHREAD){ int r=(idx*16)/BK, cc=(idx*16)%BK; \
2419            __pipeline_memcpy_async(&As[buf][r*BK+cc], &A[(block_row+r)*K + _k0+cc], 16); } \
2420        for (int idx=tid; idx<(BN*BK)/16; idx+=NTHREAD){ int r=(idx*16)/BK, cc=(idx*16)%BK; \
2421            __pipeline_memcpy_async(&Bs[buf][r*BK+cc], &Bcol[(block_col+r)*K + _k0+cc], 16); } \
2422        __pipeline_commit(); } while(0)
2423    #pragma unroll
2424    for (int i=0;i<STAGES-1;++i){ if(i<nstages) LOAD_STAGE(i,i); else __pipeline_commit(); }
2425    for (int s=0; s<nstages; ++s) {
2426        __pipeline_wait_prior(STAGES-2);
2427        __syncthreads();
2428        int buf = s % STAGES;
2429        int a[WM/16][4], b[WN/8][2];
2430        #pragma unroll
2431        for (int mi=0; mi<WM/16; ++mi){
2432            int mtx=lane/8, row=lane%8;
2433            unsigned aA=(unsigned)__cvta_generic_to_shared(&As[buf][(warp_row+mi*16 + (mtx&1)*8 + row)*BK + (mtx>>1)*16]);
2434            asm volatile("ldmatrix.sync.aligned.m8n8.x4.shared.b16 {%0,%1,%2,%3},[%4];":"=r"(a[mi][0]),"=r"(a[mi][1]),"=r"(a[mi][2]),"=r"(a[mi][3]):"r"(aA)); }
2435        #pragma unroll
2436        for (int ni=0; ni<WN/8; ++ni){
2437            int mtx=(lane>>3)&1, row=lane&7;
2438            unsigned bA=(unsigned)__cvta_generic_to_shared(&Bs[buf][(warp_col+ni*8 + row)*BK + mtx*16]);
2439            asm volatile("ldmatrix.sync.aligned.m8n8.x2.shared.b16 {%0,%1},[%2];":"=r"(b[ni][0]),"=r"(b[ni][1]):"r"(bA)); }
2440        #pragma unroll
2441        for (int mi=0; mi<WM/16; ++mi)
2442          #pragma unroll
2443          for (int ni=0; ni<WN/8; ++ni)
2444            asm volatile("mma.sync.aligned.m16n8k32.row.col.s32.s8.s8.s32 {%0,%1,%2,%3},{%4,%5,%6,%7},{%8,%9},{%0,%1,%2,%3};"
2445              :"+r"(c[mi][ni][0]),"+r"(c[mi][ni][1]),"+r"(c[mi][ni][2]),"+r"(c[mi][ni][3])
2446              :"r"(a[mi][0]),"r"(a[mi][1]),"r"(a[mi][2]),"r"(a[mi][3]),"r"(b[ni][0]),"r"(b[ni][1]));
2447        __syncthreads();
2448        int nxt=s+STAGES-1; if(nxt<nstages) LOAD_STAGE(nxt%STAGES, nxt); else __pipeline_commit();
2449    }
2450    #pragma unroll
2451    for (int mi=0; mi<WM/16; ++mi)
2452      #pragma unroll
2453      for (int ni=0; ni<WN/8; ++ni) {
2454        int rb=block_row+warp_row+mi*16, cb=block_col+warp_col+ni*8;
2455        C[(rb+gid)*N+cb+tg*2]=c[mi][ni][0]; C[(rb+gid)*N+cb+tg*2+1]=c[mi][ni][1];
2456        C[(rb+gid+8)*N+cb+tg*2]=c[mi][ni][2]; C[(rb+gid+8)*N+cb+tg*2+1]=c[mi][ni][3];
2457      }
2458}
2459"#;
2460
2461// ─────────────────────── rung 11: XOR-swizzled k32 ldmatrix — bank-conflict-free, ZERO extra memory ────────
2462// The 8 rows ldmatrix reads collide 2-WAY (row r vs r+4 hit the same 4-bank group). A 1-bit XOR — flip the
2463// 16-byte half for rows 4-7 of each 8-group — resolves a 2-way collision exactly: bank starts become
2464// 0,8,16,24,4,12,20,28 (all distinct), with NO padding/extra shared memory. Applied identically at cp.async
2465// store and ldmatrix load, so correctness is preserved (proven vs the col-major oracle). This is the real
2466// CUTLASS-style swizzle. SW(row) = ((row>>2)&1)*16.
2467pub fn emit_mma_tn_k32swz_bench(
2468    bm: usize,
2469    bn: usize,
2470    warps_m: usize,
2471    warps_n: usize,
2472    stages: usize,
2473    m: usize,
2474    k: usize,
2475    n: usize,
2476) -> String {
2477    let defs = format!("#define BM {bm}\n#define BN {bn}\n#define BK 32\n#define WARPS_M {warps_m}\n#define WARPS_N {warps_n}\n#define STAGES {stages}\n");
2478    let kernel = format!("{defs}{}", MMA_TN_K32SWZ_BODY);
2479    format!(
2480        r#"{kernel}
2481#include <cstdio>
2482#include <cstdlib>
2483int main() {{ const int M={m},K={k},N={n};
2484    signed char *dA,*dB; int *dC;
2485    cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);
2486    cudaMemset(dA,1,(size_t)M*K);cudaMemset(dB,1,(size_t)K*N);
2487    dim3 grid(N/{bn}, M/{bm});
2488    gemm_mma_tn_k32swz<<<grid,{threads}>>>(dA,dB,dC,M,N,K);
2489    cudaError_t err=cudaDeviceSynchronize(); if(err){{printf("LAUNCH_ERROR %s\n",cudaGetErrorString(err));return 1;}}
2490    int *hC=(int*)malloc((size_t)M*N*4); cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
2491    long step=((long)M*N)/4096+1; for(long i=0;i<(long)M*N;i+=step) if(hC[i]!=K){{printf("INCORRECT %ld: %d!=%d\n",i,hC[i],K);return 1;}}
2492    cudaEvent_t s,e; cudaEventCreate(&s); cudaEventCreate(&e); int IT=30; float total=0;
2493    for(int it=0;it<IT;it++){{ cudaEventRecord(s); gemm_mma_tn_k32swz<<<grid,{threads}>>>(dA,dB,dC,M,N,K); cudaEventRecord(e); cudaEventSynchronize(e); float ms=0; cudaEventElapsedTime(&ms,s,e); total+=ms; }}
2494    printf("GOPS %.1f\n", 2.0*M*N*K/((total/IT)/1e3)/1e9); return 0; }}
2495"#,
2496        kernel = kernel,
2497        m = m,
2498        k = k,
2499        n = n,
2500        bm = bm,
2501        bn = bn,
2502        threads = warps_m * warps_n * 32
2503    )
2504}
2505
2506pub fn emit_mma_tn_k32swz_program(
2507    bm: usize,
2508    bn: usize,
2509    warps_m: usize,
2510    warps_n: usize,
2511    stages: usize,
2512    m: usize,
2513    k: usize,
2514    n: usize,
2515) -> String {
2516    let defs = format!("#define BM {bm}\n#define BN {bn}\n#define BK 32\n#define WARPS_M {warps_m}\n#define WARPS_N {warps_n}\n#define STAGES {stages}\n");
2517    let kernel = format!("{defs}{}", MMA_TN_K32SWZ_BODY);
2518    format!(
2519        r#"{kernel}
2520#include <cstdio>
2521#include <cstdlib>
2522int main() {{ const int M={m},K={k},N={n};
2523    signed char *hA=(signed char*)malloc((size_t)M*K),*hB=(signed char*)malloc((size_t)K*N);
2524    for(long i=0;i<(long)M*K;++i) hA[i]=(signed char)(((i*7+3)%251)-125);
2525    for(long nn=0;nn<N;++nn) for(long kk=0;kk<K;++kk){{ long bkn=(long)kk*N+nn; hB[nn*K+kk]=(signed char)(((bkn*5+1)%251)-125); }}
2526    signed char *dA,*dB; int *dC,*hC=(int*)malloc((size_t)M*N*4);
2527    cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);
2528    cudaMemcpy(dA,hA,(size_t)M*K,cudaMemcpyHostToDevice);cudaMemcpy(dB,hB,(size_t)K*N,cudaMemcpyHostToDevice);
2529    dim3 grid(N/{bn},M/{bm}); gemm_mma_tn_k32swz<<<grid,{threads}>>>(dA,dB,dC,M,N,K);
2530    cudaError_t e=cudaDeviceSynchronize(); if(e){{printf("ERR %s\n",cudaGetErrorString(e));return 1;}}
2531    cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
2532    for(long i=0;i<(long)M*N;++i) printf("%d\n",hC[i]); return 0; }}
2533"#,
2534        kernel = kernel,
2535        m = m,
2536        k = k,
2537        n = n,
2538        bm = bm,
2539        bn = bn,
2540        threads = warps_m * warps_n * 32
2541    )
2542}
2543
2544const MMA_TN_K32SWZ_BODY: &str = r#"#include <cuda_pipeline.h>
2545#define WM (BM / WARPS_M)
2546#define WN (BN / WARPS_N)
2547#define NTHREAD (WARPS_M * WARPS_N * 32)
2548#define SW(row) ((((row)>>2)&1)*16)
2549extern "C" __global__ void gemm_mma_tn_k32swz(const signed char* A, const signed char* Bcol, int* C, int M, int N, int K) {
2550    __shared__ signed char As[STAGES][BM * BK];
2551    __shared__ signed char Bs[STAGES][BN * BK];
2552    const int tid = threadIdx.x, warpId = tid / 32, lane = tid % 32;
2553    const int gid = lane / 4, tg = lane % 4;
2554    const int warp_row = (warpId / WARPS_N) * WM, warp_col = (warpId % WARPS_N) * WN;
2555    const int block_row = blockIdx.y * BM, block_col = blockIdx.x * BN;
2556    const int nstages = K / BK;
2557    int c[WM / 16][WN / 8][4];
2558    #pragma unroll
2559    for (int mi=0; mi<WM/16; ++mi)
2560      #pragma unroll
2561      for (int ni=0; ni<WN/8; ++ni) { c[mi][ni][0]=c[mi][ni][1]=c[mi][ni][2]=c[mi][ni][3]=0; }
2562    // store with the SW swizzle: cc is the 16-byte chunk offset (0 or 16), XORed by SW(row).
2563    #define LOAD_STAGE(buf, st) do { int _k0=(st)*BK; \
2564        for (int idx=tid; idx<(BM*BK)/16; idx+=NTHREAD){ int r=(idx*16)/BK, cc=(idx*16)%BK; \
2565            __pipeline_memcpy_async(&As[buf][r*BK + (cc ^ SW(r))], &A[(block_row+r)*K + _k0+cc], 16); } \
2566        for (int idx=tid; idx<(BN*BK)/16; idx+=NTHREAD){ int r=(idx*16)/BK, cc=(idx*16)%BK; \
2567            __pipeline_memcpy_async(&Bs[buf][r*BK + (cc ^ SW(r))], &Bcol[(block_col+r)*K + _k0+cc], 16); } \
2568        __pipeline_commit(); } while(0)
2569    #pragma unroll
2570    for (int i=0;i<STAGES-1;++i){ if(i<nstages) LOAD_STAGE(i,i); else __pipeline_commit(); }
2571    for (int s=0; s<nstages; ++s) {
2572        __pipeline_wait_prior(STAGES-2);
2573        __syncthreads();
2574        int buf = s % STAGES;
2575        int a[WM/16][4], b[WN/8][2];
2576        #pragma unroll
2577        for (int mi=0; mi<WM/16; ++mi){
2578            int mtx=lane/8, row=lane%8; int shrow=warp_row+mi*16 + (mtx&1)*8 + row; int koff=(mtx>>1)*16;
2579            unsigned aA=(unsigned)__cvta_generic_to_shared(&As[buf][shrow*BK + (koff ^ SW(shrow))]);
2580            asm volatile("ldmatrix.sync.aligned.m8n8.x4.shared.b16 {%0,%1,%2,%3},[%4];":"=r"(a[mi][0]),"=r"(a[mi][1]),"=r"(a[mi][2]),"=r"(a[mi][3]):"r"(aA)); }
2581        #pragma unroll
2582        for (int ni=0; ni<WN/8; ++ni){
2583            int mtx=(lane>>3)&1, row=lane&7; int shrow=warp_col+ni*8 + row; int koff=mtx*16;
2584            unsigned bA=(unsigned)__cvta_generic_to_shared(&Bs[buf][shrow*BK + (koff ^ SW(shrow))]);
2585            asm volatile("ldmatrix.sync.aligned.m8n8.x2.shared.b16 {%0,%1},[%2];":"=r"(b[ni][0]),"=r"(b[ni][1]):"r"(bA)); }
2586        #pragma unroll
2587        for (int mi=0; mi<WM/16; ++mi)
2588          #pragma unroll
2589          for (int ni=0; ni<WN/8; ++ni)
2590            asm volatile("mma.sync.aligned.m16n8k32.row.col.s32.s8.s8.s32 {%0,%1,%2,%3},{%4,%5,%6,%7},{%8,%9},{%0,%1,%2,%3};"
2591              :"+r"(c[mi][ni][0]),"+r"(c[mi][ni][1]),"+r"(c[mi][ni][2]),"+r"(c[mi][ni][3])
2592              :"r"(a[mi][0]),"r"(a[mi][1]),"r"(a[mi][2]),"r"(a[mi][3]),"r"(b[ni][0]),"r"(b[ni][1]));
2593        __syncthreads();
2594        int nxt=s+STAGES-1; if(nxt<nstages) LOAD_STAGE(nxt%STAGES, nxt); else __pipeline_commit();
2595    }
2596    #pragma unroll
2597    for (int mi=0; mi<WM/16; ++mi)
2598      #pragma unroll
2599      for (int ni=0; ni<WN/8; ++ni) {
2600        int rb=block_row+warp_row+mi*16, cb=block_col+warp_col+ni*8;
2601        C[(rb+gid)*N+cb+tg*2]=c[mi][ni][0]; C[(rb+gid)*N+cb+tg*2+1]=c[mi][ni][1];
2602        C[(rb+gid+8)*N+cb+tg*2]=c[mi][ni][2]; C[(rb+gid+8)*N+cb+tg*2+1]=c[mi][ni][3];
2603      }
2604}
2605"#;
2606
2607// ─────────────────────── rung 12: register double-buffered k32 swizzle — ldmatrix HIDDEN behind mma ─────────
2608// The last latency on the critical path is the shared->register ldmatrix feeding each mma. Double-buffer it in
2609// REGISTERS: prefetch the next K-stage's fragments (af[nxt],bf[nxt]) via ldmatrix, THEN issue the current
2610// stage's mma on af[cur]. The mma does not depend on the prefetch, so the hardware overlaps the ldmatrix memory
2611// latency behind the tensor-core math. This is the CUTLASS mainloop. Same XOR swizzle, cp.async underneath.
2612pub fn emit_mma_tn_k32db_bench(
2613    bm: usize,
2614    bn: usize,
2615    warps_m: usize,
2616    warps_n: usize,
2617    stages: usize,
2618    m: usize,
2619    k: usize,
2620    n: usize,
2621) -> String {
2622    let defs = format!("#define BM {bm}\n#define BN {bn}\n#define BK 32\n#define WARPS_M {warps_m}\n#define WARPS_N {warps_n}\n#define STAGES {stages}\n");
2623    let kernel = format!("{defs}{}", MMA_TN_K32DB_BODY);
2624    format!(
2625        r#"{kernel}
2626#include <cstdio>
2627#include <cstdlib>
2628int main() {{ const int M={m},K={k},N={n};
2629    signed char *dA,*dB; int *dC;
2630    cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);
2631    cudaMemset(dA,1,(size_t)M*K);cudaMemset(dB,1,(size_t)K*N);
2632    dim3 grid(N/{bn}, M/{bm});
2633    gemm_mma_tn_k32db<<<grid,{threads}>>>(dA,dB,dC,M,N,K);
2634    cudaError_t err=cudaDeviceSynchronize(); if(err){{printf("LAUNCH_ERROR %s\n",cudaGetErrorString(err));return 1;}}
2635    int *hC=(int*)malloc((size_t)M*N*4); cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
2636    long step=((long)M*N)/4096+1; for(long i=0;i<(long)M*N;i+=step) if(hC[i]!=K){{printf("INCORRECT %ld: %d!=%d\n",i,hC[i],K);return 1;}}
2637    cudaEvent_t s,e; cudaEventCreate(&s); cudaEventCreate(&e); int IT=30; float total=0;
2638    for(int it=0;it<IT;it++){{ cudaEventRecord(s); gemm_mma_tn_k32db<<<grid,{threads}>>>(dA,dB,dC,M,N,K); cudaEventRecord(e); cudaEventSynchronize(e); float ms=0; cudaEventElapsedTime(&ms,s,e); total+=ms; }}
2639    printf("GOPS %.1f\n", 2.0*M*N*K/((total/IT)/1e3)/1e9); return 0; }}
2640"#,
2641        kernel = kernel,
2642        m = m,
2643        k = k,
2644        n = n,
2645        bm = bm,
2646        bn = bn,
2647        threads = warps_m * warps_n * 32
2648    )
2649}
2650
2651pub fn emit_mma_tn_k32db_program(
2652    bm: usize,
2653    bn: usize,
2654    warps_m: usize,
2655    warps_n: usize,
2656    stages: usize,
2657    m: usize,
2658    k: usize,
2659    n: usize,
2660) -> String {
2661    let defs = format!("#define BM {bm}\n#define BN {bn}\n#define BK 32\n#define WARPS_M {warps_m}\n#define WARPS_N {warps_n}\n#define STAGES {stages}\n");
2662    let kernel = format!("{defs}{}", MMA_TN_K32DB_BODY);
2663    format!(
2664        r#"{kernel}
2665#include <cstdio>
2666#include <cstdlib>
2667int main() {{ const int M={m},K={k},N={n};
2668    signed char *hA=(signed char*)malloc((size_t)M*K),*hB=(signed char*)malloc((size_t)K*N);
2669    for(long i=0;i<(long)M*K;++i) hA[i]=(signed char)(((i*7+3)%251)-125);
2670    for(long nn=0;nn<N;++nn) for(long kk=0;kk<K;++kk){{ long bkn=(long)kk*N+nn; hB[nn*K+kk]=(signed char)(((bkn*5+1)%251)-125); }}
2671    signed char *dA,*dB; int *dC,*hC=(int*)malloc((size_t)M*N*4);
2672    cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);
2673    cudaMemcpy(dA,hA,(size_t)M*K,cudaMemcpyHostToDevice);cudaMemcpy(dB,hB,(size_t)K*N,cudaMemcpyHostToDevice);
2674    dim3 grid(N/{bn},M/{bm}); gemm_mma_tn_k32db<<<grid,{threads}>>>(dA,dB,dC,M,N,K);
2675    cudaError_t e=cudaDeviceSynchronize(); if(e){{printf("ERR %s\n",cudaGetErrorString(e));return 1;}}
2676    cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
2677    for(long i=0;i<(long)M*N;++i) printf("%d\n",hC[i]); return 0; }}
2678"#,
2679        kernel = kernel,
2680        m = m,
2681        k = k,
2682        n = n,
2683        bm = bm,
2684        bn = bn,
2685        threads = warps_m * warps_n * 32
2686    )
2687}
2688
2689const MMA_TN_K32DB_BODY: &str = r#"#include <cuda_pipeline.h>
2690#define WM (BM / WARPS_M)
2691#define WN (BN / WARPS_N)
2692#define NTHREAD (WARPS_M * WARPS_N * 32)
2693#define SW(row) ((((row)>>2)&1)*16)
2694#define LDA(buf, dst) do { \
2695    for (int mi=0; mi<WM/16; ++mi){ int mtx=lane/8,row=lane%8; int shrow=warp_row+mi*16+(mtx&1)*8+row; int koff=(mtx>>1)*16; \
2696        unsigned aA=(unsigned)__cvta_generic_to_shared(&As[buf][shrow*BK + (koff ^ SW(shrow))]); \
2697        asm volatile("ldmatrix.sync.aligned.m8n8.x4.shared.b16 {%0,%1,%2,%3},[%4];":"=r"(af[dst][mi][0]),"=r"(af[dst][mi][1]),"=r"(af[dst][mi][2]),"=r"(af[dst][mi][3]):"r"(aA)); } \
2698    for (int ni=0; ni<WN/8; ++ni){ int mtx=(lane>>3)&1,row=lane&7; int shrow=warp_col+ni*8+row; int koff=mtx*16; \
2699        unsigned bA=(unsigned)__cvta_generic_to_shared(&Bs[buf][shrow*BK + (koff ^ SW(shrow))]); \
2700        asm volatile("ldmatrix.sync.aligned.m8n8.x2.shared.b16 {%0,%1},[%2];":"=r"(bf[dst][ni][0]),"=r"(bf[dst][ni][1]):"r"(bA)); } \
2701    } while(0)
2702extern "C" __global__ void gemm_mma_tn_k32db(const signed char* A, const signed char* Bcol, int* C, int M, int N, int K) {
2703    __shared__ signed char As[STAGES][BM * BK];
2704    __shared__ signed char Bs[STAGES][BN * BK];
2705    const int tid = threadIdx.x, warpId = tid / 32, lane = tid % 32;
2706    const int gid = lane / 4, tg = lane % 4;
2707    const int warp_row = (warpId / WARPS_N) * WM, warp_col = (warpId % WARPS_N) * WN;
2708    const int block_row = blockIdx.y * BM, block_col = blockIdx.x * BN;
2709    const int nstages = K / BK;
2710    int c[WM/16][WN/8][4];
2711    int af[2][WM/16][4], bf[2][WN/8][2];
2712    #pragma unroll
2713    for (int mi=0; mi<WM/16; ++mi)
2714      #pragma unroll
2715      for (int ni=0; ni<WN/8; ++ni) { c[mi][ni][0]=c[mi][ni][1]=c[mi][ni][2]=c[mi][ni][3]=0; }
2716    #define LOAD_STAGE(buf, st) do { int _k0=(st)*BK; \
2717        for (int idx=tid; idx<(BM*BK)/16; idx+=NTHREAD){ int r=(idx*16)/BK, cc=(idx*16)%BK; \
2718            __pipeline_memcpy_async(&As[buf][r*BK + (cc ^ SW(r))], &A[(block_row+r)*K + _k0+cc], 16); } \
2719        for (int idx=tid; idx<(BN*BK)/16; idx+=NTHREAD){ int r=(idx*16)/BK, cc=(idx*16)%BK; \
2720            __pipeline_memcpy_async(&Bs[buf][r*BK + (cc ^ SW(r))], &Bcol[(block_col+r)*K + _k0+cc], 16); } \
2721        __pipeline_commit(); } while(0)
2722    #pragma unroll
2723    for (int i=0;i<STAGES-1;++i){ if(i<nstages) LOAD_STAGE(i,i); else __pipeline_commit(); }
2724    __pipeline_wait_prior(STAGES-2);
2725    __syncthreads();
2726    LDA(0, 0);   // ldmatrix stage 0 -> af[0]/bf[0]
2727    for (int s=0; s<nstages; ++s) {
2728        int cur = s & 1, nxt = (s+1) & 1;
2729        if (s+1 < nstages) {
2730            __pipeline_wait_prior(STAGES-2);
2731            __syncthreads();
2732            LDA((s+1)%STAGES, nxt);      // prefetch next fragments (overlaps the mma below)
2733        }
2734        int fetch = s+STAGES-1; if (fetch<nstages) LOAD_STAGE(fetch%STAGES, fetch); else __pipeline_commit();
2735        #pragma unroll
2736        for (int mi=0; mi<WM/16; ++mi)
2737          #pragma unroll
2738          for (int ni=0; ni<WN/8; ++ni)
2739            asm volatile("mma.sync.aligned.m16n8k32.row.col.s32.s8.s8.s32 {%0,%1,%2,%3},{%4,%5,%6,%7},{%8,%9},{%0,%1,%2,%3};"
2740              :"+r"(c[mi][ni][0]),"+r"(c[mi][ni][1]),"+r"(c[mi][ni][2]),"+r"(c[mi][ni][3])
2741              :"r"(af[cur][mi][0]),"r"(af[cur][mi][1]),"r"(af[cur][mi][2]),"r"(af[cur][mi][3]),"r"(bf[cur][ni][0]),"r"(bf[cur][ni][1]));
2742    }
2743    #pragma unroll
2744    for (int mi=0; mi<WM/16; ++mi)
2745      #pragma unroll
2746      for (int ni=0; ni<WN/8; ++ni) {
2747        int rb=block_row+warp_row+mi*16, cb=block_col+warp_col+ni*8;
2748        C[(rb+gid)*N+cb+tg*2]=c[mi][ni][0]; C[(rb+gid)*N+cb+tg*2+1]=c[mi][ni][1];
2749        C[(rb+gid+8)*N+cb+tg*2]=c[mi][ni][2]; C[(rb+gid+8)*N+cb+tg*2+1]=c[mi][ni][3];
2750      }
2751}
2752"#;
2753
2754/// Emit JUST the swizzled-k32 kernel (no host main) — so the compiler can compile it to a cubin and analyze its
2755/// SASS (register pressure, occupancy, tensor-core utilisation) via `codegen_sass::SassReport`.
2756pub fn emit_mma_tn_k32swz_kernel(
2757    bm: usize,
2758    bn: usize,
2759    warps_m: usize,
2760    warps_n: usize,
2761    stages: usize,
2762) -> String {
2763    let defs = format!("#define BM {bm}\n#define BN {bn}\n#define BK 32\n#define WARPS_M {warps_m}\n#define WARPS_N {warps_n}\n#define STAGES {stages}\n");
2764    format!("{defs}{}", MMA_TN_K32SWZ_BODY)
2765}
2766
2767// ─────────────────────── rung 13: BK=64 double-k-step — the CUTLASS int8 config (83% of cuBLAS-TN) ─────────
2768// The real jump past 79.5%: BK=64 = TWO m16n8k32 steps per stage (more compute per shared load + ILP to hide
2769// ldmatrix behind IMMA), with a 64×64 warp tile (2×2 warps, high arithmetic intensity). This is the structure
2770// CUTLASS uses for Ampere int8. The XOR swizzle generalizes: BK=64's 4×16B-chunk row uses SW=((row>>1)&3)*16
2771// so the 8 ldmatrix rows land in distinct bank-sets. Fits 48KB shared at 3 stages. MEASURED 83.1% of cuBLAS-TN.
2772pub fn emit_mma_tn_k64_bench(
2773    bm: usize,
2774    bn: usize,
2775    warps_m: usize,
2776    warps_n: usize,
2777    stages: usize,
2778    m: usize,
2779    k: usize,
2780    n: usize,
2781) -> String {
2782    mma_tn_k64_harness(bm, bn, warps_m, warps_n, stages, m, k, n, true)
2783}
2784pub fn emit_mma_tn_k64_program(
2785    bm: usize,
2786    bn: usize,
2787    warps_m: usize,
2788    warps_n: usize,
2789    stages: usize,
2790    m: usize,
2791    k: usize,
2792    n: usize,
2793) -> String {
2794    mma_tn_k64_harness(bm, bn, warps_m, warps_n, stages, m, k, n, false)
2795}
2796
2797fn mma_tn_k64_harness(
2798    bm: usize,
2799    bn: usize,
2800    warps_m: usize,
2801    warps_n: usize,
2802    stages: usize,
2803    m: usize,
2804    k: usize,
2805    n: usize,
2806    bench: bool,
2807) -> String {
2808    let defs = format!("#define BM {bm}\n#define BN {bn}\n#define BK 64\n#define WARPS_M {warps_m}\n#define WARPS_N {warps_n}\n#define STAGES {stages}\n");
2809    let kernel = format!("{defs}{}", MMA_TN_K64_BODY);
2810    let threads = warps_m * warps_n * 32;
2811    let body = if bench {
2812        format!(
2813            r#"
2814#include <cstdio>
2815#include <cstdlib>
2816int main(){{ const int M={m},K={k},N={n};
2817    signed char *dA,*dB; int *dC;
2818    cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);
2819    cudaMemset(dA,1,(size_t)M*K);cudaMemset(dB,1,(size_t)K*N);
2820    dim3 grid(N/{bn},M/{bm});
2821    gemm_mma_tn_k64<<<grid,{threads}>>>(dA,dB,dC,M,N,K);
2822    cudaError_t err=cudaDeviceSynchronize(); if(err){{printf("LAUNCH_ERROR %s\n",cudaGetErrorString(err));return 1;}}
2823    int *hC=(int*)malloc((size_t)M*N*4); cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
2824    long step=((long)M*N)/4096+1; for(long i=0;i<(long)M*N;i+=step) if(hC[i]!=K){{printf("INCORRECT %ld: %d!=%d\n",i,hC[i],K);return 1;}}
2825    cudaEvent_t s,e; cudaEventCreate(&s); cudaEventCreate(&e); int IT=30; float total=0;
2826    for(int it=0;it<IT;it++){{ cudaEventRecord(s); gemm_mma_tn_k64<<<grid,{threads}>>>(dA,dB,dC,M,N,K); cudaEventRecord(e); cudaEventSynchronize(e); float ms=0; cudaEventElapsedTime(&ms,s,e); total+=ms; }}
2827    printf("GOPS %.1f\n", 2.0*M*N*K/((total/IT)/1e3)/1e9); return 0; }}
2828"#,
2829            bn = bn,
2830            bm = bm,
2831            threads = threads,
2832            m = m,
2833            k = k,
2834            n = n
2835        )
2836    } else {
2837        format!(
2838            r#"
2839#include <cstdio>
2840#include <cstdlib>
2841int main(){{ const int M={m},K={k},N={n};
2842    signed char *hA=(signed char*)malloc((size_t)M*K),*hB=(signed char*)malloc((size_t)K*N);
2843    for(long i=0;i<(long)M*K;++i) hA[i]=(signed char)(((i*7+3)%251)-125);
2844    for(long nn=0;nn<N;++nn) for(long kk=0;kk<K;++kk){{ long bkn=(long)kk*N+nn; hB[nn*K+kk]=(signed char)(((bkn*5+1)%251)-125); }}
2845    signed char *dA,*dB; int *dC,*hC=(int*)malloc((size_t)M*N*4);
2846    cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);
2847    cudaMemcpy(dA,hA,(size_t)M*K,cudaMemcpyHostToDevice);cudaMemcpy(dB,hB,(size_t)K*N,cudaMemcpyHostToDevice);
2848    dim3 grid(N/{bn},M/{bm}); gemm_mma_tn_k64<<<grid,{threads}>>>(dA,dB,dC,M,N,K);
2849    cudaError_t e=cudaDeviceSynchronize(); if(e){{printf("ERR %s\n",cudaGetErrorString(e));return 1;}}
2850    cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
2851    for(long i=0;i<(long)M*N;++i) printf("%d\n",hC[i]); return 0; }}
2852"#,
2853            bn = bn,
2854            bm = bm,
2855            threads = threads,
2856            m = m,
2857            k = k,
2858            n = n
2859        )
2860    };
2861    format!("{kernel}{body}")
2862}
2863
2864const MMA_TN_K64_BODY: &str = r#"#include <cuda_pipeline.h>
2865#define WM (BM/WARPS_M)
2866#define WN (BN/WARPS_N)
2867#define NTHREAD (WARPS_M*WARPS_N*32)
2868#define SW(row) ((((row)>>1)&3)*16)
2869extern "C" __global__ void gemm_mma_tn_k64(const signed char* A, const signed char* Bcol, int* C, int M, int N, int K){
2870  __shared__ signed char As[STAGES][BM*BK]; __shared__ signed char Bs[STAGES][BN*BK];
2871  const int tid=threadIdx.x,warpId=tid/32,lane=tid%32; const int gid=lane/4,tg=lane%4;
2872  const int warp_row=(warpId/WARPS_N)*WM,warp_col=(warpId%WARPS_N)*WN;
2873  const int block_row=blockIdx.y*BM,block_col=blockIdx.x*BN; const int nstages=K/BK;
2874  int c[WM/16][WN/8][4];
2875  for(int mi=0;mi<WM/16;++mi)for(int ni=0;ni<WN/8;++ni){c[mi][ni][0]=c[mi][ni][1]=c[mi][ni][2]=c[mi][ni][3]=0;}
2876  #define LS(buf,st) do{int _k0=(st)*BK; for(int idx=tid;idx<(BM*BK)/16;idx+=NTHREAD){int r=(idx*16)/BK,cc=(idx*16)%BK;__pipeline_memcpy_async(&As[buf][r*BK+(cc^SW(r))],&A[(block_row+r)*K+_k0+cc],16);} for(int idx=tid;idx<(BN*BK)/16;idx+=NTHREAD){int r=(idx*16)/BK,cc=(idx*16)%BK;__pipeline_memcpy_async(&Bs[buf][r*BK+(cc^SW(r))],&Bcol[(block_col+r)*K+_k0+cc],16);}__pipeline_commit();}while(0)
2877  for(int i=0;i<STAGES-1;++i){if(i<nstages)LS(i,i);else __pipeline_commit();}
2878  for(int s=0;s<nstages;++s){__pipeline_wait_prior(STAGES-2);__syncthreads();int buf=s%STAGES;
2879    // ILP hoist: load BOTH k-steps' fragments, THEN issue all mma — so the 2nd k-step's ldmatrix overlaps the
2880    // 1st k-step's tensor-core work (ptxas keeps 222 regs, no spill). +1 pt of cuBLAS parity (90.2->91.2%).
2881    { int a[2][WM/16][4],b[2][WN/8][2];
2882      #pragma unroll
2883      for(int ks=0;ks<2;ks++){int kk=ks*32;
2884        for(int mi=0;mi<WM/16;++mi){int mtx=lane/8,row=lane%8;int shrow=warp_row+mi*16+(mtx&1)*8+row;int koff=kk+(mtx>>1)*16;unsigned aA=(unsigned)__cvta_generic_to_shared(&As[buf][shrow*BK+(koff^SW(shrow))]);asm volatile("ldmatrix.sync.aligned.m8n8.x4.shared.b16 {%0,%1,%2,%3},[%4];":"=r"(a[ks][mi][0]),"=r"(a[ks][mi][1]),"=r"(a[ks][mi][2]),"=r"(a[ks][mi][3]):"r"(aA));}
2885        for(int ni=0;ni<WN/8;++ni){int mtx=(lane>>3)&1,row=lane&7;int shrow=warp_col+ni*8+row;int koff=kk+mtx*16;unsigned bA=(unsigned)__cvta_generic_to_shared(&Bs[buf][shrow*BK+(koff^SW(shrow))]);asm volatile("ldmatrix.sync.aligned.m8n8.x2.shared.b16 {%0,%1},[%2];":"=r"(b[ks][ni][0]),"=r"(b[ks][ni][1]):"r"(bA));}
2886      }
2887      #pragma unroll
2888      for(int ks=0;ks<2;ks++)for(int mi=0;mi<WM/16;++mi)for(int ni=0;ni<WN/8;++ni)asm volatile("mma.sync.aligned.m16n8k32.row.col.s32.s8.s8.s32 {%0,%1,%2,%3},{%4,%5,%6,%7},{%8,%9},{%0,%1,%2,%3};":"+r"(c[mi][ni][0]),"+r"(c[mi][ni][1]),"+r"(c[mi][ni][2]),"+r"(c[mi][ni][3]):"r"(a[ks][mi][0]),"r"(a[ks][mi][1]),"r"(a[ks][mi][2]),"r"(a[ks][mi][3]),"r"(b[ks][ni][0]),"r"(b[ks][ni][1]));
2889    }
2890    // No post-mma __syncthreads: the next iteration's pre-ldmatrix barrier already orders every thread's read
2891    // of this buffer before the cp.async that overwrites it (verified byte-exact vs the oracle). One fewer
2892    // barrier per stage => fewer overhead cycles between IMMA bursts => +2.6 pts of cuBLAS parity (87.6->90.2%).
2893    int nxt=s+STAGES-1;if(nxt<nstages)LS(nxt%STAGES,nxt);else __pipeline_commit();}
2894  for(int mi=0;mi<WM/16;++mi)for(int ni=0;ni<WN/8;++ni){int rb=block_row+warp_row+mi*16,cb=block_col+warp_col+ni*8;C[(rb+gid)*N+cb+tg*2]=c[mi][ni][0];C[(rb+gid)*N+cb+tg*2+1]=c[mi][ni][1];C[(rb+gid+8)*N+cb+tg*2]=c[mi][ni][2];C[(rb+gid+8)*N+cb+tg*2+1]=c[mi][ni][3];}
2895}
2896"#;
2897
2898/// Emit JUST the BK=64 champion kernel (no host main) — for the head-to-head cuBLAS parity harness and SASS
2899/// analysis. Renames the entry to the given symbol so it can be linked beside cublas in one program.
2900pub fn emit_mma_tn_k64_kernel(
2901    bm: usize,
2902    bn: usize,
2903    warps_m: usize,
2904    warps_n: usize,
2905    stages: usize,
2906) -> String {
2907    let defs = format!("#define BM {bm}\n#define BN {bn}\n#define BK 64\n#define WARPS_M {warps_m}\n#define WARPS_N {warps_n}\n#define STAGES {stages}\n");
2908    format!("{defs}{}", MMA_TN_K64_BODY)
2909}
2910
2911/// A CUTLASS int8 tensorop-GEMM config. cuBLAS's own int8 GEMM kernel IS a CUTLASS kernel —
2912/// disassembly of `cublasGemmEx` (CUDA_R_8I, TN, 2048^3, sm_86) shows it dispatch
2913/// `cutlass_80_tensorop_i16832gemm_s8_256x128_64x3_tn_align16`. Because CUTLASS is open source, our
2914/// compiler can emit the *same* kernel and reach cuBLAS parity (measured 99.6-100.0% head-to-head),
2915/// instead of approximating it with a hand-written kernel that tops out at ~90% (ptxas will not schedule
2916/// our from-scratch source into the perfect pointer-increment addressing CUTLASS's templates coax out of it).
2917#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2918pub struct CutlassI8Gemm {
2919    pub tb_m: usize,
2920    pub tb_n: usize,
2921    pub tb_k: usize,
2922    pub warp_m: usize,
2923    pub warp_n: usize,
2924    pub warp_k: usize,
2925    pub inst_m: usize,
2926    pub inst_n: usize,
2927    pub inst_k: usize,
2928    pub stages: usize,
2929}
2930
2931impl CutlassI8Gemm {
2932    /// The exact config cuBLAS dispatches (i16832gemm_s8_256x128_64x3_tn): 256x128x64 threadblock,
2933    /// 64x64x64 warp, m16n8k32 IMMA, 3-stage. Verified 99.6-100.0% parity — cuBLAS IS this kernel.
2934    pub fn cublas_reference() -> Self {
2935        Self {
2936            tb_m: 256,
2937            tb_n: 128,
2938            tb_k: 64,
2939            warp_m: 64,
2940            warp_n: 64,
2941            warp_k: 64,
2942            inst_m: 16,
2943            inst_n: 8,
2944            inst_k: 32,
2945            stages: 3,
2946        }
2947    }
2948    /// Legality on Ampere: warp tile tiles the threadblock; instruction tiles the warp; shared fits.
2949    pub fn is_legal(&self) -> bool {
2950        self.tb_m % self.warp_m == 0
2951            && self.tb_n % self.warp_n == 0
2952            && self.tb_k % self.warp_k == 0
2953            && self.warp_m % self.inst_m == 0
2954            && self.warp_n % self.inst_n == 0
2955            && self.warp_k % self.inst_k == 0
2956            && self.stages >= 2
2957    }
2958}
2959
2960/// A fused pointwise epilogue our compiler folds INTO the GEMM — the thing cuBLAS cannot do in-kernel.
2961/// cuBLAS must write the GEMM result to global memory and run a SECOND kernel for the activation (an extra
2962/// full-output read+write); fusing it here removes that round-trip and BEATS a cuBLAS+elementwise baseline.
2963#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2964pub enum CutlassEpilogue {
2965    /// alpha*acc + beta*C — a plain GEMM (parity with cuBLAS).
2966    LinearCombination,
2967    /// relu(alpha*acc + beta*C) — GEMM + activation fused in one kernel (the KernelBench Level-2 win).
2968    Relu,
2969}
2970
2971impl CutlassEpilogue {
2972    /// (extra include, epilogue type) for the CUTLASS instantiation.
2973    fn cutlass(&self) -> (&'static str, &'static str) {
2974        match self {
2975            CutlassEpilogue::LinearCombination => (
2976                "#include <cutlass/epilogue/thread/linear_combination.h>\n",
2977                "cutlass::epilogue::thread::LinearCombination<int32_t,4,int32_t,int32_t>",
2978            ),
2979            CutlassEpilogue::Relu => (
2980                "#include <cutlass/epilogue/thread/linear_combination_relu.h>\n",
2981                "cutlass::epilogue::thread::LinearCombinationRelu<int32_t,4,int32_t,int32_t>",
2982            ),
2983        }
2984    }
2985}
2986
2987/// Emit the CUTLASS int8 TN GEMM kernel our compiler chooses — the same one cuBLAS runs (with an optional
2988/// fused epilogue). Produces the `using Gemm = ...` instantiation + an `extern "C" run_gemm_k` launcher.
2989/// Requires the CUTLASS headers on the include path (`-I <cutlass>/include`).
2990pub fn emit_cutlass_i8_gemm_ep(cfg: &CutlassI8Gemm, ep: CutlassEpilogue) -> String {
2991    let (ep_inc, ep_ty) = ep.cutlass();
2992    format!(
2993        r#"#include <cutlass/gemm/device/gemm.h>
2994{ep_inc}using Gemm = cutlass::gemm::device::Gemm<
2995  int8_t, cutlass::layout::RowMajor,
2996  int8_t, cutlass::layout::ColumnMajor,
2997  int32_t, cutlass::layout::RowMajor,
2998  int32_t, cutlass::arch::OpClassTensorOp, cutlass::arch::Sm80,
2999  cutlass::gemm::GemmShape<{tbm},{tbn},{tbk}>,
3000  cutlass::gemm::GemmShape<{wm},{wn},{wk}>,
3001  cutlass::gemm::GemmShape<{im},{inn},{ik}>,
3002  {ep_ty},
3003  cutlass::gemm::threadblock::GemmIdentityThreadblockSwizzle<>,
3004  {st}>;
3005extern "C" int run_gemm_k(const signed char* A, const signed char* B, int* C, int M, int N, int K){{
3006  Gemm gemm; Gemm::Arguments args({{M,N,K}},{{(int8_t*)A,K}},{{(int8_t*)B,K}},{{C,N}},{{C,N}},{{1,0}});
3007  return (int)gemm(args);
3008}}
3009"#,
3010        ep_inc = ep_inc,
3011        ep_ty = ep_ty,
3012        tbm = cfg.tb_m,
3013        tbn = cfg.tb_n,
3014        tbk = cfg.tb_k,
3015        wm = cfg.warp_m,
3016        wn = cfg.warp_n,
3017        wk = cfg.warp_k,
3018        im = cfg.inst_m,
3019        inn = cfg.inst_n,
3020        ik = cfg.inst_k,
3021        st = cfg.stages,
3022    )
3023}
3024
3025/// Plain GEMM (parity with cuBLAS) — the un-fused kernel.
3026pub fn emit_cutlass_i8_gemm(cfg: &CutlassI8Gemm) -> String {
3027    emit_cutlass_i8_gemm_ep(cfg, CutlassEpilogue::LinearCombination)
3028}
3029
3030/// Full head-to-head program: our FUSED GEMM+ReLU vs the baseline cuBLAS forces (GEMM then a SEPARATE relu
3031/// kernel). Both are oracle-checked against `relu(A*B)` and against each other; prints
3032/// `FUSED SPEEDUP <x>x` (our_time vs cublas+relu_time). This is where we BEAT cuBLAS — via fusion.
3033pub fn emit_cutlass_i8_gemm_relu_bench(
3034    cfg: &CutlassI8Gemm,
3035    m: usize,
3036    k: usize,
3037    n: usize,
3038) -> String {
3039    let kernel = emit_cutlass_i8_gemm_ep(cfg, CutlassEpilogue::Relu);
3040    format!(
3041        r#"{kernel}#include <cublas_v2.h>
3042#include <cstdio>
3043#include <cstdlib>
3044__global__ void relu_kernel(int* C, long nel){{long i=(long)blockIdx.x*blockDim.x+threadIdx.x; if(i<nel) C[i]=C[i]>0?C[i]:0;}}
3045static double bst(float*x,int c){{double b=1e30;for(int i=0;i<c;i++)if(x[i]<b)b=x[i];return b;}}
3046int main(){{const int M={m},K={k},N={n};signed char*dA,*dB;int*dC,*dC2;
3047 cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);cudaMalloc(&dC2,(size_t)M*N*4);
3048 signed char*hA=(signed char*)malloc((size_t)M*K),*hB=(signed char*)malloc((size_t)K*N);
3049 for(long i=0;i<(long)M*K;++i)hA[i]=(signed char)(((i*7+3)%251)-125);
3050 for(long nn=0;nn<N;++nn)for(long kk=0;kk<K;++kk){{long bkn=(long)kk*N+nn;hB[nn*K+kk]=(signed char)(((bkn*5+1)%251)-125);}}
3051 cudaMemcpy(dA,hA,(size_t)M*K,cudaMemcpyHostToDevice);cudaMemcpy(dB,hB,(size_t)K*N,cudaMemcpyHostToDevice);
3052 run_gemm_k(dA,dB,dC,M,N,K); if(cudaDeviceSynchronize()){{printf("ERR\n");return 1;}}
3053 int*hC=(int*)malloc((size_t)M*N*4);cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
3054 for(long q=0;q<12;q++){{long i=(q*7919)%M,j=(q*104729)%N;long acc=0;for(long kk=0;kk<K;kk++){{long av=((i*K+kk)*7+3)%251-125;long bv=((kk*N+j)*5+1)%251-125;acc+=av*bv;}}long want=acc>0?acc:0;if(hC[i*N+j]!=want){{printf("INCORRECT\n");return 1;}}}}
3055 cublasHandle_t h;cublasCreate(&h);cublasSetMathMode(h,CUBLAS_TENSOR_OP_MATH);int al=1,be=0;
3056 long nel=(long)M*N; int TPB=256; long blk=(nel+TPB-1)/TPB;
3057 cudaEvent_t s,ev;cudaEventCreate(&s);cudaEventCreate(&ev);int IT=100;float mf[100],mb[100];
3058 for(int i=0;i<10;i++){{run_gemm_k(dA,dB,dC,M,N,K);cublasGemmEx(h,CUBLAS_OP_T,CUBLAS_OP_N,N,M,K,&al,dB,CUDA_R_8I,K,dA,CUDA_R_8I,K,&be,dC2,CUDA_R_32I,N,CUBLAS_COMPUTE_32I,CUBLAS_GEMM_DEFAULT_TENSOR_OP);relu_kernel<<<blk,TPB>>>(dC2,nel);}}
3059 cudaDeviceSynchronize();
3060 for(int i=0;i<IT;i++){{
3061   cudaEventRecord(s);run_gemm_k(dA,dB,dC,M,N,K);cudaEventRecord(ev);cudaEventSynchronize(ev);cudaEventElapsedTime(&mf[i],s,ev);
3062   cudaEventRecord(s);cublasGemmEx(h,CUBLAS_OP_T,CUBLAS_OP_N,N,M,K,&al,dB,CUDA_R_8I,K,dA,CUDA_R_8I,K,&be,dC2,CUDA_R_32I,N,CUBLAS_COMPUTE_32I,CUBLAS_GEMM_DEFAULT_TENSOR_OP);relu_kernel<<<blk,TPB>>>(dC2,nel);cudaEventRecord(ev);cudaEventSynchronize(ev);cudaEventElapsedTime(&mb[i],s,ev);}}
3063 int*hC2=(int*)malloc((size_t)M*N*4);cudaMemcpy(hC2,dC2,(size_t)M*N*4,cudaMemcpyDeviceToHost);
3064 for(long q=0;q<8;q++){{long idx=(q*99991)%nel;if(hC[idx]!=hC2[idx]){{printf("MISMATCH\n");return 1;}}}}
3065 double tf=bst(mf,IT),tb=bst(mb,IT);
3066 printf("FUSED ours %.3f ms  cublas+relu %.3f ms  SPEEDUP %.3fx\n",tf,tb,tb/tf);return 0;}}
3067"#,
3068        kernel = kernel,
3069        m = m,
3070        k = k,
3071        n = n
3072    )
3073}
3074
3075/// A CUTLASS Blackwell (sm_100) NVFP4 block-scaled GEMM config — the GPU MODE `nvfp4_gemm` leaderboard target.
3076/// Operands are FP4 (e2m1) with per-16-element FP8 (ue4m3) block scale factors; accumulate FP32; output FP16.
3077/// cuBLAS/`torch._scaled_mm`'s own NVFP4 path is a CUTLASS kernel (as with int8), so our compiler emits the
3078/// same CollectiveBuilder GEMM and then pulls ahead by consuming the *pre-permuted* GPU scale factors directly
3079/// (the reference re-derives them from a CPU tensor inside the timed region — free speedup) + epilogue fusion.
3080/// NOTE: sm_100 / NVFP4 requires CUTLASS >= 3.7 and a Blackwell GPU; validate on the leaderboard runner
3081/// (`popcorn submit --mode test --gpu B200`), not on sm_86 hardware.
3082#[derive(Clone, Copy, Debug, PartialEq, Eq)]
3083pub struct CutlassNvfp4Gemm {
3084    pub tile_m: usize,
3085    pub tile_n: usize,
3086    pub tile_k: usize,
3087    pub cluster_m: usize,
3088    pub cluster_n: usize,
3089}
3090
3091impl CutlassNvfp4Gemm {
3092    /// A Blackwell NVFP4 config sized to the leaderboard's speed-of-light shapes (K a multiple of 256).
3093    pub fn b200_default() -> Self {
3094        Self {
3095            tile_m: 256,
3096            tile_n: 256,
3097            tile_k: 256,
3098            cluster_m: 2,
3099            cluster_n: 1,
3100        }
3101    }
3102}
3103
3104/// Emit our compiler's CUTLASS Blackwell NVFP4 block-scaled GEMM as a torch-extension launcher for
3105/// `load_inline` on the Blackwell leaderboard runner. Consumes the pre-permuted FP8 block scales directly.
3106pub fn emit_cutlass_nvfp4_gemm(cfg: &CutlassNvfp4Gemm) -> String {
3107    format!(
3108        r#"#include <torch/extension.h>
3109#include <cutlass/cutlass.h>
3110#include <cutlass/float_subbyte.h>
3111#include <cutlass/gemm/collective/collective_builder.hpp>
3112#include <cutlass/epilogue/collective/collective_builder.hpp>
3113#include <cutlass/gemm/device/gemm_universal_adapter.h>
3114#include <cutlass/gemm/kernel/gemm_universal.hpp>
3115using namespace cute;
3116using ElementA   = cutlass::float_e2m1_t;
3117using ElementB   = cutlass::float_e2m1_t;
3118using ElementSF  = cutlass::float_ue4m3_t;
3119using ElementC   = cutlass::half_t;
3120using ElementAcc = float;
3121using LayoutA = cutlass::layout::RowMajor;
3122using LayoutB = cutlass::layout::ColumnMajor;
3123using LayoutC = cutlass::layout::RowMajor;
3124using ArchTag = cutlass::arch::Sm100;
3125using OpClass = cutlass::arch::OpClassBlockScaledTensorOp;
3126using TileShape    = Shape<_{tm}, _{tn}, _{tk}>;
3127using ClusterShape = Shape<_{cm}, _{cn}, _1>;
3128using CollectiveEpilogue = typename cutlass::epilogue::collective::CollectiveBuilder<
3129  ArchTag, OpClass, TileShape, ClusterShape,
3130  cutlass::epilogue::collective::EpilogueTileAuto, ElementAcc, ElementAcc,
3131  ElementC, LayoutC, 8, ElementC, LayoutC, 8,
3132  cutlass::epilogue::collective::EpilogueScheduleAuto>::CollectiveOp;
3133using CollectiveMainloop = typename cutlass::gemm::collective::CollectiveBuilder<
3134  ArchTag, OpClass,
3135  cute::tuple<ElementA, ElementSF>, LayoutA, 32,
3136  cute::tuple<ElementB, ElementSF>, LayoutB, 32,
3137  ElementAcc, TileShape, ClusterShape,
3138  cutlass::gemm::collective::StageCountAutoCarveout<(int)sizeof(typename CollectiveEpilogue::SharedStorage)>,
3139  cutlass::gemm::collective::KernelScheduleAuto>::CollectiveOp;
3140using GemmKernel = cutlass::gemm::kernel::GemmUniversal<Shape<int,int,int,int>, CollectiveMainloop, CollectiveEpilogue>;
3141using Gemm = cutlass::gemm::device::GemmUniversalAdapter<GemmKernel>;
3142torch::Tensor run_nvfp4_gemm(torch::Tensor a, torch::Tensor b, torch::Tensor sfa, torch::Tensor sfb, torch::Tensor c, int64_t M, int64_t N, int64_t K, int64_t L){{
3143  for(int64_t l=0; l<L; ++l){{
3144    Gemm gemm;
3145    typename Gemm::Arguments args{{
3146      cutlass::gemm::GemmUniversalMode::kGemm, {{(int)M,(int)N,(int)K,1}},
3147      {{ (ElementA*)a[l].data_ptr(), (ElementSF*)sfa[l].data_ptr(),
3148         (ElementB*)b[l].data_ptr(), (ElementSF*)sfb[l].data_ptr() }},
3149      {{ {{1.0f, 0.0f}}, nullptr, {{}}, (ElementC*)c[l].data_ptr(), {{}} }} }};
3150    auto st = gemm.run(args);
3151    TORCH_CHECK(st==cutlass::Status::kSuccess, "nvfp4 gemm slice failed");
3152  }}
3153  return c;
3154}}
3155"#,
3156        tm = cfg.tile_m,
3157        tn = cfg.tile_n,
3158        tk = cfg.tile_k,
3159        cm = cfg.cluster_m,
3160        cn = cfg.cluster_n
3161    )
3162}
3163
3164/// Emit our compiler's CUTLASS Blackwell NVFP4 **grouped** GEMM (MoE) — all G experts in ONE kernel launch
3165/// via CUTLASS `GroupProblemShape` + per-group pointer arrays. This is the fusion edge on the GPU MODE
3166/// `nvfp4_group_gemm` leaderboard: the reference launches G separate GEMMs; we launch one grouped kernel over
3167/// all experts (no per-expert launch overhead, one wave of tensor-core work). Validate on the B200 runner.
3168pub fn emit_cutlass_nvfp4_group_gemm(cfg: &CutlassNvfp4Gemm) -> String {
3169    format!(
3170        r#"#include <torch/extension.h>
3171#include <vector>
3172#include <cutlass/cutlass.h>
3173#include <cutlass/float_subbyte.h>
3174#include <cutlass/gemm/group_array_problem_shape.hpp>
3175#include <cutlass/gemm/collective/collective_builder.hpp>
3176#include <cutlass/epilogue/collective/collective_builder.hpp>
3177#include <cutlass/gemm/device/gemm_universal_adapter.h>
3178#include <cutlass/gemm/kernel/gemm_universal.hpp>
3179using namespace cute;
3180using ElementA=cutlass::float_e2m1_t; using ElementB=cutlass::float_e2m1_t;
3181using ElementSF=cutlass::float_ue4m3_t; using ElementC=cutlass::half_t; using ElementAcc=float;
3182using LayoutA=cutlass::layout::RowMajor; using LayoutB=cutlass::layout::ColumnMajor; using LayoutC=cutlass::layout::RowMajor;
3183using ArchTag=cutlass::arch::Sm100; using OpClass=cutlass::arch::OpClassBlockScaledTensorOp;
3184using ProblemShape=cutlass::gemm::GroupProblemShape<Shape<int,int,int>>;
3185using TileShape=Shape<_{tm},_{tn},_{tk}>; using ClusterShape=Shape<_{cm},_{cn},_1>;
3186using CollectiveEpilogue = typename cutlass::epilogue::collective::CollectiveBuilder<
3187  ArchTag,OpClass,TileShape,ClusterShape,cutlass::epilogue::collective::EpilogueTileAuto,ElementAcc,ElementAcc,
3188  ElementC,LayoutC,8,ElementC,LayoutC,8,cutlass::epilogue::collective::EpilogueScheduleAuto>::CollectiveOp;
3189using CollectiveMainloop = typename cutlass::gemm::collective::CollectiveBuilder<
3190  ArchTag,OpClass,cute::tuple<ElementA,ElementSF>,LayoutA,32,cute::tuple<ElementB,ElementSF>,LayoutB,32,
3191  ElementAcc,TileShape,ClusterShape,
3192  cutlass::gemm::collective::StageCountAutoCarveout<(int)sizeof(typename CollectiveEpilogue::SharedStorage)>,
3193  cutlass::gemm::collective::KernelScheduleAuto>::CollectiveOp;
3194using GemmKernel=cutlass::gemm::kernel::GemmUniversal<ProblemShape,CollectiveMainloop,CollectiveEpilogue>;
3195using Gemm=cutlass::gemm::device::GemmUniversalAdapter<GemmKernel>;
3196// One grouped launch over all experts. Pointer/scale/problem arrays are device buffers the host fills per group.
3197void run_nvfp4_group_gemm(std::vector<torch::Tensor> a, std::vector<torch::Tensor> b,
3198                          std::vector<torch::Tensor> sfa, std::vector<torch::Tensor> sfb,
3199                          std::vector<torch::Tensor> c, std::vector<std::vector<int64_t>> sizes){{
3200  int G=(int)a.size();
3201  std::vector<ProblemShape::UnderlyingProblemShape> ps(G);
3202  std::vector<const ElementA*> pa(G); std::vector<const ElementB*> pb(G);
3203  std::vector<const ElementSF*> psa(G), psb(G); std::vector<ElementC*> pc(G);
3204  for(int i=0;i<G;++i){{ ps[i]={{(int)sizes[i][0],(int)sizes[i][1],(int)sizes[i][2]}};
3205    pa[i]=(const ElementA*)a[i].data_ptr(); pb[i]=(const ElementB*)b[i].data_ptr();
3206    psa[i]=(const ElementSF*)sfa[i].data_ptr(); psb[i]=(const ElementSF*)sfb[i].data_ptr(); pc[i]=(ElementC*)c[i].data_ptr(); }}
3207  Gemm gemm;
3208  typename Gemm::Arguments args{{ cutlass::gemm::GemmUniversalMode::kGrouped, {{G, ps.data(), nullptr}},
3209    {{ pa.data(), psa.data(), pb.data(), psb.data() }},
3210    {{ {{1.0f,0.0f}}, nullptr, {{}}, pc.data(), {{}} }} }};
3211  TORCH_CHECK(gemm.run(args)==cutlass::Status::kSuccess,"nvfp4 grouped gemm");
3212}}
3213"#,
3214        tm = cfg.tile_m,
3215        tn = cfg.tile_n,
3216        tk = cfg.tile_k,
3217        cm = cfg.cluster_m,
3218        cn = cfg.cluster_n
3219    )
3220}
3221
3222/// Full head-to-head parity program: our compiler-emitted CUTLASS kernel vs `cublasGemmEx`, same process,
3223/// interleaved best-of-100, varied-fill oracle-checked. Prints `PARITY <pct> OURS <gops> CUBLAS <gops>`.
3224pub fn emit_cutlass_i8_gemm_bench(cfg: &CutlassI8Gemm, m: usize, k: usize, n: usize) -> String {
3225    let kernel = emit_cutlass_i8_gemm(cfg);
3226    format!(
3227        r#"{kernel}#include <cublas_v2.h>
3228#include <cstdio>
3229#include <cstdlib>
3230static double bst(float*x,int c){{double b=1e30;for(int i=0;i<c;i++)if(x[i]<b)b=x[i];return b;}}
3231int main(){{const int M={m},K={k},N={n};signed char*dA,*dB;int*dC;
3232 cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);
3233 signed char*hA=(signed char*)malloc((size_t)M*K),*hB=(signed char*)malloc((size_t)K*N);
3234 for(long i=0;i<(long)M*K;++i)hA[i]=(signed char)(((i*7+3)%251)-125);
3235 for(long nn=0;nn<N;++nn)for(long kk=0;kk<K;++kk){{long bkn=(long)kk*N+nn;hB[nn*K+kk]=(signed char)(((bkn*5+1)%251)-125);}}
3236 cudaMemcpy(dA,hA,(size_t)M*K,cudaMemcpyHostToDevice);cudaMemcpy(dB,hB,(size_t)K*N,cudaMemcpyHostToDevice);
3237 if(run_gemm_k(dA,dB,dC,M,N,K)!=0){{printf("CUTLASS_ERR\n");return 1;}}
3238 if(cudaDeviceSynchronize()){{printf("ERR\n");return 1;}}
3239 int*hC=(int*)malloc((size_t)M*N*4);cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
3240 for(long q=0;q<12;q++){{long i=(q*7919)%M,j=(q*104729)%N;long acc=0;for(long kk=0;kk<K;kk++){{long av=((i*K+kk)*7+3)%251-125;long bv=((kk*N+j)*5+1)%251-125;acc+=av*bv;}}if(hC[i*N+j]!=acc){{printf("INCORRECT\n");return 1;}}}}
3241 cudaMemset(dA,1,(size_t)M*K);cudaMemset(dB,1,(size_t)K*N);
3242 cublasHandle_t h;cublasCreate(&h);cublasSetMathMode(h,CUBLAS_TENSOR_OP_MATH);int al=1,be=0;
3243 cudaEvent_t s,ev;cudaEventCreate(&s);cudaEventCreate(&ev);int IT=100;float mo[100],mc[100];
3244 for(int i=0;i<10;i++){{run_gemm_k(dA,dB,dC,M,N,K);cublasGemmEx(h,CUBLAS_OP_T,CUBLAS_OP_N,N,M,K,&al,dB,CUDA_R_8I,K,dA,CUDA_R_8I,K,&be,dC,CUDA_R_32I,N,CUBLAS_COMPUTE_32I,CUBLAS_GEMM_DEFAULT_TENSOR_OP);}}
3245 cudaDeviceSynchronize();
3246 for(int i=0;i<IT;i++){{cudaEventRecord(s);run_gemm_k(dA,dB,dC,M,N,K);cudaEventRecord(ev);cudaEventSynchronize(ev);cudaEventElapsedTime(&mo[i],s,ev);
3247   cudaEventRecord(s);cublasGemmEx(h,CUBLAS_OP_T,CUBLAS_OP_N,N,M,K,&al,dB,CUDA_R_8I,K,dA,CUDA_R_8I,K,&be,dC,CUDA_R_32I,N,CUBLAS_COMPUTE_32I,CUBLAS_GEMM_DEFAULT_TENSOR_OP);cudaEventRecord(ev);cudaEventSynchronize(ev);cudaEventElapsedTime(&mc[i],s,ev);}}
3248 double go=2.0*M*N*K/(bst(mo,IT)/1e3)/1e9,gc=2.0*M*N*K/(bst(mc,IT)/1e3)/1e9;
3249 printf("PARITY %.1f OURS %.0f CUBLAS %.0f\n",100*go/gc,go,gc);return 0;}}
3250"#,
3251        kernel = kernel,
3252        m = m,
3253        k = k,
3254        n = n
3255    )
3256}