kernelbench.com

KernelBench hard · H100

W4A16 GEMM Claude Opus 5

34.4%geomean peak fraction across shapes

manually audited: clean

Three real kernel families for int4 group-128 dequant GEMM: lop3-dequant mma.sync path, wgmma transposed-operand path for large M (weights stay in registers), M==1 GEMV with split-K atomic-ticket reduction; autotuned, identity-keyed graph replay (x is self._x; any new tensor rebinds and recomputes). ADJUDICATED HIT: a comment reasons about check.py's fixed atol=1.0 large_activation case -- context shows the OPPOSITE of tolerance gaming: folding scale out of the dequant skips the reference's bf16 rounding and FAILS stress, so the agent deliberately rounds w to bf16 first to reproduce the reference's numerics exactly. Legitimate numerics engineering informed by the readable checker; check.py never edited. Grader files Read-only, template_mutated false, no foreign-archive access. Passed check.py + stress on the isolated re-grade; clean 0.3439 vs contended 0.2754 (+24.9% -- contention was suppressing this cell).

harnessor-opusagent session14h 58mtotal wall14h 60mcheck9sbenchmark6soutput tokensgpu-lock wait8mgpu-lock held2h 24mregimememory

Per-shape vs governing ceilingeach shape graded against whichever binds — bf16 compute or HBM bandwidth

1×12288×40960.027 ms48.2%0.98 TB/s · 48% of 2.0 TB/s HBM · also 4 TFLOPS (0% of compute)
32×12288×40960.037 ms37.0%0.75 TB/s · 37% of 2.0 TB/s HBM · also 87 TFLOPS (12% of compute)
256×12288×40960.072 ms23.8%356 TFLOPS · 47% of 756 TF bf16 peak · also 0.49 TB/s (24% of HBM)
1×4096×40960.018 ms24.0%0.49 TB/s · 24% of 2.0 TB/s HBM · also 2 TFLOPS (0% of compute)
16×14336×40960.033 ms47.3%0.97 TB/s · 47% of 2.0 TB/s HBM · also 57 TFLOPS (8% of compute)

compute-bound memory-bound · bar + right column = official fraction of the ceiling (the geomean input)

geomean(48.2% · 37.0% · 23.8% · 24.0% · 47.3%) = 34.4%

Kernel source (redacted)
"""Fused W4A16 (AWQ/GPTQ-style asymmetric int4, group=128) GEMM for SM90.

One kernel does everything: the packed int4 weights are streamed straight from
HBM into registers in an mma-fragment-ready layout, unpacked with a single
`lop3` per bf16x2, zero-point-corrected with one `sub.rn.bf16x2`, and fed to
`mma.sync.aligned.m16n8k16.row.col.f32.bf16.bf16.f32`.

The activation tile lives in shared memory for a whole K-chunk (the whole K when
it fits), so the steady-state inner loop contains no barriers at all -- warps
drift apart and cover each other's HBM latency.  The per-group bf16 scale is
folded into the fp32 accumulator once every 128 k (small M), or into the bf16 B
fragment with one `mul.rn.bf16x2` (large M, where it halves register pressure
and reproduces the reference's own bf16 rounding).

Above M=64 that shape inverts.  `mma.sync` tops out near half of what the card
can do, so the large-M tile switches to `wgmma` with the operands transposed --
the dequantized weights stay in registers as the A operand and the activations
become the smem B operand, which is the only arrangement where 25MB of int4
never round-trips through shared memory.  It needs activations pre-permuted
into descriptor order (one small pass over x, weights untouched), and it is
twice as fast at M=256.  Autotuning picks between the two per shape.
"""
from __future__ import annotations

import os
import statistics

import torch
import torch.nn as nn

GROUP_SIZE = 128

# --------------------------------------------------------------------------- #
# CUDA source
# --------------------------------------------------------------------------- #

_CUDA = r'''
#include <cuda_runtime.h>
#include <cuda_bf16.h>
#include <cstdint>

#define DEVI __device__ __forceinline__

// ((w >> sh) & 0x000F000F) | 0x43004300  ->  bf16x2 == {128+v_lo, 128+v_hi}
DEVI uint32_t dq(uint32_t w, int sh) {
  uint32_t t = w >> sh;
  uint32_t h;
  asm("lop3.b32 %0, %1, %2, %3, 0xEA;"
      : "=r"(h) : "r"(t), "n"(0x000F000FU), "n"(0x43004300U));
  return h;
}

DEVI uint32_t bsub(uint32_t a, uint32_t b) {
  uint32_t d;
  asm("sub.rn.bf16x2 %0, %1, %2;" : "=r"(d) : "r"(a), "r"(b));
  return d;
}

DEVI uint32_t bmul(uint32_t a, uint32_t b) {
  uint32_t d;
  asm("mul.rn.bf16x2 %0, %1, %2;" : "=r"(d) : "r"(a), "r"(b));
  return d;
}

DEVI float bf2f(uint32_t h) {          // low 16 bits hold a bf16
  uint32_t x = h << 16;
  float f;
  asm("mov.b32 %0, %1;" : "=f"(f) : "r"(x));
  return f;
}

DEVI uint32_t bcast16(const __nv_bfloat16* p) {     // v -> {v, v}
  const uint32_t v = *reinterpret_cast<const uint16_t*>(p);
  return (v << 16) | v;
}

// splat one half of a packed {zero,scale} word across a bf16x2: 0x1010 takes
// bytes {0,1,0,1} (the low half twice), 0x3232 takes {2,3,2,3}.
DEVI uint32_t prmt2(uint32_t v, uint32_t sel) {
  uint32_t d; asm("prmt.b32 %0, %1, 0, %2;" : "=r"(d) : "r"(v), "r"(sel)); return d;
}

DEVI uint32_t lds32(const __nv_bfloat16* p) {
  return *reinterpret_cast<const uint32_t*>(p);
}

DEVI void mma16816(float* d, const uint32_t* a, const uint32_t* b) {
  asm volatile(
      "mma.sync.aligned.m16n8k16.row.col.f32.bf16.bf16.f32 "
      "{%0,%1,%2,%3}, {%4,%5,%6,%7}, {%8,%9}, {%0,%1,%2,%3};\n"
      : "+f"(d[0]), "+f"(d[1]), "+f"(d[2]), "+f"(d[3])
      : "r"(a[0]), "r"(a[1]), "r"(a[2]), "r"(a[3]), "r"(b[0]), "r"(b[1]));
}

// ---- extra primitives used by the big-M kernel below ----------------------
DEVI void ldm4(uint32_t* d, uint32_t sa) {      // one 16x16 A fragment
  asm volatile("ldmatrix.sync.aligned.m8n8.x4.shared.b16 {%0,%1,%2,%3}, [%4];\n"
      : "=r"(d[0]), "=r"(d[1]), "=r"(d[2]), "=r"(d[3]) : "r"(sa));
}
DEVI void cpa16(uint32_t dst, const void* src) {
  asm volatile("cp.async.cg.shared.global [%0], [%1], 16;\n" :: "r"(dst), "l"(src) : "memory");
}
// .ca keeps the line in L1 as well.  The activation tile is re-read by every
// block in the n direction, so on a 2-blocks-per-SM config the second block
// hits L1 -- a .cg copy would throw that reuse away.
DEVI void cpa16ca(uint32_t dst, const void* src) {
  asm volatile("cp.async.ca.shared.global [%0], [%1], 16;\n" :: "r"(dst), "l"(src) : "memory");
}
DEVI void cpcommit() { asm volatile("cp.async.commit_group;\n" ::: "memory"); }
template<int G> DEVI void cpwait() {
  asm volatile("cp.async.wait_group %0;\n" :: "n"(G) : "memory");
}
// ---- bulk async copy (TMA), 1D non-tensor form ----------------------------
// Once the activations are pre-permuted a staged chunk is one contiguous 32KB
// run, which is exactly what cp.async.bulk moves: a single instruction from a
// single thread hands the whole run to the DMA engine instead of metering it
// through the per-thread 16B cp.async issue port (measured at 1-2 copies per
// cycle per SM).  Measured 93.1 -> 86.1 us at M=256, and it takes staging off
// the critical path entirely (staging-free ablation is 85.5 us).
DEVI void mbinit(uint32_t bar, int cnt) {
  asm volatile("mbarrier.init.shared::cta.b64 [%0], %1;\n"
               :: "r"(bar), "r"(cnt) : "memory");
}
DEVI void mbexpect(uint32_t bar, uint32_t bytes) {
  asm volatile("{\n.reg .b64 s_;\n"
               "mbarrier.arrive.expect_tx.shared::cta.b64 s_, [%0], %1;\n}\n"
               :: "r"(bar), "r"(bytes) : "memory");
}
DEVI void bulkcp(uint32_t dst, const void* src, uint32_t bytes, uint32_t bar) {
  asm volatile("cp.async.bulk.shared::cluster.global"
               ".mbarrier::complete_tx::bytes [%0], [%1], %2, [%3];\n"
               :: "r"(dst), "l"(src), "r"(bytes), "r"(bar) : "memory");
}
// try_wait in a C++ loop rather than a bra loop inside the asm: PTX labels are
// function-scoped, so an inlined helper carrying one duplicates it.
DEVI void mbwait(uint32_t bar, int phase) {
  uint32_t ok;
  do {
    asm volatile("{\n.reg .pred p_;\n"
                 "mbarrier.try_wait.parity.shared::cta.b64 p_, [%1], %2;\n"
                 "selp.b32 %0, 1, 0, p_;\n}\n"
                 : "=r"(ok) : "r"(bar), "r"(phase) : "memory");
  } while (!ok);
}
DEVI uint32_t pk2(float a, float b) {           // {a,b} -> bf16x2
  uint32_t d;
  asm("cvt.rn.bf16x2.f32 %0, %1, %2;" : "=r"(d) : "f"(b), "f"(a));
  return d;
}

// ---- extra primitives used by the M==1 kernel below -----------------------
DEVI uint32_t bfma(uint32_t a, uint32_t b, uint32_t c) {
  uint32_t d;
  asm("fma.rn.bf16x2 %0, %1, %2, %3;" : "=r"(d) : "r"(a), "r"(b), "r"(c));
  return d;
}
DEVI uint4 lds4(const void* p) {
  uint4 v;
  asm("ld.shared.v4.u32 {%0,%1,%2,%3}, [%4];"
      : "=r"(v.x), "=r"(v.y), "=r"(v.z), "=r"(v.w)
      : "r"((uint32_t)__cvta_generic_to_shared(p)));
  return v;
}
DEVI float4 lds4f(const float* p) {
  float4 v;
  asm("ld.shared.v4.f32 {%0,%1,%2,%3}, [%4];"
      : "=f"(v.x), "=f"(v.y), "=f"(v.z), "=f"(v.w)
      : "r"((uint32_t)__cvta_generic_to_shared(p)));
  return v;
}
// The harness flushes L2 with a 128MB memset before every timed launch, so the
// cache starts full of dirty lines that have to be written back as our reads
// allocate over them -- 14us of the 26us this kernel takes on shape0.  A weight
// stream has no reuse to lose, so mark our own lines as the preferred victims
// and the dirty ones survive instead of draining to DRAM.  Worth 12-25%.
// (sm_90 ptxas only accepts .L2::eviction_priority directly on .v8.b32 loads
// and has no .L2::no_allocate, so the hint has to ride in a policy register.)
DEVI uint64_t pol_evict_first() {
  uint64_t p;
  asm volatile("createpolicy.fractional.L2::evict_first.b64 %0, 1.0;" : "=l"(p));
  return p;
}
DEVI uint4 ldg4h(const void* p, uint64_t pol) {
  uint4 v;
  asm("ld.global.nc.L2::cache_hint.v4.u32 {%0,%1,%2,%3}, [%4], %5;"
      : "=r"(v.x), "=r"(v.y), "=r"(v.z), "=r"(v.w) : "l"(p), "l"(pol));
  return v;
}
DEVI uint4 ldg4(const void* p) {
  uint4 v;
  asm("ld.global.nc.v4.u32 {%0,%1,%2,%3}, [%4];"
      : "=r"(v.x), "=r"(v.y), "=r"(v.z), "=r"(v.w) : "l"(p));
  return v;
}
// The hint is only right where a block reads its B slice exactly once; with more
// than one row-block per column strip that slice has real L2 reuse to keep.
template<int EV> DEVI uint4 ldgb(const void* p, uint64_t pol) {
  return EV ? ldg4h(p, pol) : ldg4(p);
}

// --------------------------------------------------------------------------
// M == 1: no tensor cores.
//
// mma.sync m16n8k16 would spend 15/16 of its work on row padding, and that
// padding is not free -- it costs more than the dequant it is meant to hide.
// So lane j owns column j and accumulates straight from the dequantised
// nibbles.  B is repacked (see _repack_gemv) so a warp's uint4 load is 512
// contiguous bytes and each lane's uint32 holds 8 consecutive-k weights of one
// column, nibble order chosen so lop3 yields (k, k+1) pairs matching a
// contiguous pair of x.
//
// Accumulation is fp32, and the scale is applied per weight rather than folded
// into a group epilogue.  Both are required, not stylistic: the reference
// materialises w_bf = (w - z) * s as a *bf16* tensor, so every weight is
// rounded to 8 mantissa bits before the matmul, and folding the scale out skips
// that rounding.  Either shortcut leaves ~0.4% of sum(|x_k w_k|) of error, which
// is invisible relatively but blows past the fixed atol=1.0 of check.py's
// large_activation case (x scaled by 64) on the output columns where the sum
// cancels to near zero.  Rounding w to bf16 first makes the bf16 x bf16 product
// exact in fp32, so the only remaining difference from the reference is the bf16
// rounding of the output itself.  Inner loop, per uint32 of weights:
// 4 lop3 + 3 shf + 4 sub + 4 mul + 8 shf + 8 ffma + 2 LDS.128.
//
// NW column blocks per warp, KS warps splitting K, CW warp columns per block,
// PD 32k-blocks of B in flight.  blockIdx.y splits K a second time, across
// blocks: at N=4096 one block per column group only fills 64 of the 114 SMs and
// the kernel runs at 56% of the bandwidth it reaches at N=12288.  The partials
// go through g_ws and the last block to arrive for a column group (atomicInc on
// g_ctr, which wraps back to 0 so the next launch starts clean) sums them.
// --------------------------------------------------------------------------

// 14336 columns x 8 splits is the widest grid the config list allows.  Static
// device storage keeps a workspace pointer out of the launcher ABI.
__device__ float g_ws[14336 * 8];
__device__ unsigned int g_ctr[512] = {0};

template<int NW, int KS, int CW, int PD>
__global__ __launch_bounds__(32 * KS * CW) void w4a16_gemv(
    const uint32_t* __restrict__ bp, const __nv_bfloat16* __restrict__ x,
    const __nv_bfloat16* __restrict__ sc, const __nv_bfloat16* __restrict__ zp,
    __nv_bfloat16* __restrict__ out, int N, int K, int KPB)
{
  constexpr int NTHR = 32 * KS * CW;
  constexpr int COLS = CW * NW * 32;
  extern __shared__ __align__(16) char sraw[];
  float* sx = reinterpret_cast<float*>(sraw);   // x widened to fp32 once
  float* sred = reinterpret_cast<float*>(sraw + KPB * 4);

  const int tid = threadIdx.x, warp = tid >> 5, lane = tid & 31;
  const int kid = warp / CW, cw = warp % CW;
  const int kbase = blockIdx.y * KPB;           // first k of this block
  const int NKB = KPB >> 5;                     // 32-k blocks handled here
  const int KBW = NKB / KS;                     // per k-split warp
  const int kb0 = kid * KBW;
  const int cb0 = blockIdx.x * (CW * NW) + cw * NW;
  const size_t cbs = (size_t)(K >> 5) * 128;    // uint32 between column blocks
  const uint64_t pol = pol_evict_first();

#pragma unroll 4
  for (int i = tid; i < KPB / 8; i += NTHR) {
    const uint4 v = reinterpret_cast<const uint4*>(x + kbase)[i];
    float4 a, b;
    a.x = bf2f(v.x); a.y = bf2f(v.x >> 16); a.z = bf2f(v.y); a.w = bf2f(v.y >> 16);
    b.x = bf2f(v.z); b.y = bf2f(v.z >> 16); b.z = bf2f(v.w); b.w = bf2f(v.w >> 16);
    reinterpret_cast<float4*>(sx)[2 * i] = a;
    reinterpret_cast<float4*>(sx)[2 * i + 1] = b;
  }
  __syncthreads();

  const uint32_t* bq = bp + (size_t)cb0 * cbs
                     + (size_t)((kbase >> 5) + kb0) * 128 + lane * 4;
  const __nv_bfloat16* zg = zp + cb0 * 32 + lane;
  const __nv_bfloat16* sg = sc + cb0 * 32 + lane;

  float accf[NW], fa[NW][4];
#pragma unroll
  for (int t = 0; t < NW; ++t) {
#pragma unroll
    for (int i = 0; i < 4; ++i) fa[t][i] = 0.f;
  }
  uint4 br[PD][NW];
#pragma unroll
  for (int d = 0; d < PD; ++d)
#pragma unroll
    for (int t = 0; t < NW; ++t)
      br[d][t] = ldg4h(bq + (size_t)d * 128 + t * cbs, pol);

  const int NGW = KBW >> 2;                     // 128-k groups per warp
#pragma unroll 1
  for (int g = 0; g < NGW; ++g) {
    const int gg = (kbase >> 7) + (kb0 >> 2) + g;
    uint32_t z2[NW], s2[NW];
#pragma unroll
    for (int t = 0; t < NW; ++t) {
      z2[t] = bcast16(zg + (size_t)gg * N + t * 32);
      s2[t] = bcast16(sg + (size_t)gg * N + t * 32);
    }
#pragma unroll
    for (int kbi = 0; kbi < 4; ++kbi) {
      const int kb = (g << 2) + kbi;
      uint4 w[NW];
#pragma unroll
      for (int t = 0; t < NW; ++t) w[t] = br[kb % PD][t];
      if (kb + PD < KBW) {
#pragma unroll
        for (int t = 0; t < NW; ++t)
          br[kb % PD][t] = ldg4h(bq + (size_t)(kb + PD) * 128 + t * cbs, pol);
      }
      const float* xk = sx + (kb0 + kb) * 32;
#pragma unroll
      for (int q = 0; q < 4; ++q) {
        const float4 x0 = lds4f(xk + q * 8), x1 = lds4f(xk + q * 8 + 4);
        const float xf[8] = {x0.x, x0.y, x0.z, x0.w, x1.x, x1.y, x1.z, x1.w};
#pragma unroll
        for (int t = 0; t < NW; ++t) {
          const uint32_t wq = (q == 0) ? w[t].x : (q == 1) ? w[t].y
                            : (q == 2) ? w[t].z : w[t].w;
#pragma unroll
          for (int i = 0; i < 4; ++i) {
            const uint32_t v = bmul(bsub(dq(wq, 4 * i), z2[t]), s2[t]);
            fa[t][i] = fmaf(bf2f(v), xf[2 * i], fa[t][i]);
            fa[t][i] = fmaf(bf2f(v >> 16), xf[2 * i + 1], fa[t][i]);
          }
        }
      }
    }
  }
#pragma unroll
  for (int t = 0; t < NW; ++t)
    accf[t] = (fa[t][0] + fa[t][1]) + (fa[t][2] + fa[t][3]);

#pragma unroll
  for (int t = 0; t < NW; ++t)
    sred[kid * COLS + (cw * NW + t) * 32 + lane] = accf[t];
  __syncthreads();
  const int c0 = blockIdx.x * COLS;
  if (gridDim.y == 1) {
    for (int c = tid; c < COLS; c += NTHR) {
      float s = 0.f;
#pragma unroll 4
      for (int q = 0; q < KS; ++q) s += sred[q * COLS + c];
      out[c0 + c] = __float2bfloat16(s);
    }
    return;
  }

  volatile float* ws = g_ws + (size_t)blockIdx.y * N + c0;
  for (int c = tid; c < COLS; c += NTHR) {
    float s = 0.f;
#pragma unroll 4
    for (int q = 0; q < KS; ++q) s += sred[q * COLS + c];
    ws[c] = s;
  }
  // The fence makes this block's partials visible device-wide before its
  // arrival is; whoever sees the last arrival therefore sees every partial.
  __threadfence();
  __shared__ bool last;
  if (tid == 0) last = (atomicInc(&g_ctr[blockIdx.x], gridDim.y - 1)
                        == gridDim.y - 1);
  __syncthreads();
  if (!last) return;
  const volatile float* wa = g_ws + c0;
  for (int c = tid; c < COLS; c += NTHR) {
    float s = 0.f;
    for (int q = 0; q < gridDim.y; ++q) s += wa[(size_t)q * N + c];
    out[c0 + c] = __float2bfloat16(s);
  }
}

//     ID  NW  KS  CW  PD  KB
#define GEMV_LIST                       \
  G( 0,  2, 16,  1,  2,  1)             \
  G( 1,  1,  8,  4,  2,  1)             \
  G( 2,  1,  8,  2,  2,  1)             \
  G( 3,  2,  8,  1,  2,  1)             \
  G( 4,  1, 16,  1,  2,  1)             \
  G( 5,  1, 16,  2,  2,  1)             \
  G( 6,  1,  8,  2,  4,  1)             \
  G( 7,  1,  8,  1,  2,  1)             \
  G( 8,  2, 16,  1,  4,  1)             \
  G( 9,  4,  8,  1,  2,  1)             \
  G(10,  1,  8,  4,  2,  2)             \
  G(11,  1,  8,  4,  2,  4)             \
  G(12,  1,  8,  2,  2,  2)             \
  G(13,  1,  8,  2,  2,  4)             \
  G(14,  2,  8,  1,  2,  2)             \
  G(15,  2,  8,  1,  2,  4)             \
  G(16,  4,  8,  1,  2,  2)             \
  G(17,  4,  8,  1,  2,  4)             \
  G(18,  1,  8,  1,  2,  4)             \
  G(19,  1, 16,  2,  2,  2)             \
  G(20,  2, 16,  1,  2,  2)             \
  G(21,  1,  4,  2,  2,  2)             \
  G(22,  1,  4,  2,  2,  4)             \
  G(23,  1,  4,  2,  2,  8)             \
  G(24,  1,  4,  4,  2,  4)             \
  G(25,  1,  4,  4,  2,  8)             \
  G(26,  2,  4,  1,  2,  4)             \
  G(27,  2,  4,  1,  2,  8)             \
  G(28,  4,  4,  1,  2,  4)             \
  G(29,  4,  4,  1,  2,  8)             \
  G(30,  1,  4,  1,  2,  8)             \
  G(31,  1,  4,  1,  4,  8)             \
  G(32,  1,  4,  1,  4,  4)             \
  G(33,  1,  4,  2,  4,  8)             \
  G(34,  1,  4,  2,  4,  4)             \
  G(35,  2,  4,  1,  4,  8)             \
  G(36,  2,  4,  1,  4,  4)             \
  G(37,  4,  4,  1,  4,  8)             \
  G(38,  4,  4,  1,  4,  4)             \
  G(39,  1,  4,  4,  4,  8)             \
  G(40,  1,  8,  2,  4,  2)             \
  G(41,  1,  8,  2,  4,  4)             \
  G(42,  1,  8,  1,  4,  2)             \
  G(43,  1,  8,  1,  4,  4)             \
  G(44,  2,  8,  1,  4,  2)             \
  G(45,  2,  8,  1,  4,  4)             \
  G(46,  4,  8,  1,  4,  2)             \
  G(47,  4,  8,  1,  4,  4)             \
  G(48,  1,  8,  4,  4,  2)             \
  G(49,  1,  4,  1,  4,  2)             \
  G(50,  1,  4,  4,  4,  4)

#define GEMV_NCFG 51
#define GEMV_BASE 2000

template<int NW, int KS, int CW, int PD>
static void run_gemv(const void* bp, const void* x, const void* sc, const void* zp,
                     void* out, int N, int K, int KB, cudaStream_t st) {
  constexpr int NTHR = 32 * KS * CW;
  constexpr int COLS = CW * NW * 32;
  const int KPB = K / KB;
  const int smem = KPB * 4 + KS * COLS * 4;   // x widened to fp32
  static int cur = -1;
  if (smem > 48 * 1024 && smem != cur) {
    cudaFuncSetAttribute((const void*)w4a16_gemv<NW, KS, CW, PD>,
                         cudaFuncAttributeMaxDynamicSharedMemorySize, smem);
    cur = smem;
  }
  w4a16_gemv<NW, KS, CW, PD><<<dim3(N / COLS, KB), NTHR, smem, st>>>(
      (const uint32_t*)bp, (const __nv_bfloat16*)x, (const __nv_bfloat16*)sc,
      (const __nv_bfloat16*)zp, (__nv_bfloat16*)out, N, K, KPB);
}

// BM  : rows per block (16 / 32 / 64)
// NW  : 32-column groups per warp (cols/warp = 32*NW)
// WN  : warps along N
// KS  : warps splitting K
// KCH : K chunks (A tile staged once per chunk; KCH==1 -> one barrier total)
// SB  : 1 -> scale folded into the bf16 B fragment, 0 -> into the fp32 acc
// PD  : 32-k slices of B in flight per warp (software pipeline depth)
// EV  : 1 -> stream B with the evict_first L2 hint
// AR  : 1 -> only A rows 0..7 can be non-zero, 2 -> rows 0..15
template<int BM, int NW, int WN, int KS, int KCH, int SB, int PD, int EV, int AR,
         int CA>
__global__ __launch_bounds__(32 * WN * KS) void w4a16_kern(
    const uint32_t* __restrict__ bp,
    const __nv_bfloat16* __restrict__ x,
    const __nv_bfloat16* __restrict__ sc,
    const __nv_bfloat16* __restrict__ zp,
    __nv_bfloat16* __restrict__ out,
    int M, int N, int K)
{
  constexpr int MT     = BM / 16;
  constexpr int NTILES = NW * 4;
  constexpr int NCOLS  = 32 * NW;
  constexpr int BN     = NCOLS * WN;
  constexpr int NTHR   = 32 * WN * KS;
  constexpr int NWRP   = NTHR / 32;

  extern __shared__ __align__(16) char smem_raw[];
  // CA==2 parks its two mbarriers in the first 16 bytes and shifts A past
  // them; sR only exists in the epilogue, long after the barriers are dead,
  // so it can keep the base of the block.
  __nv_bfloat16* sA = reinterpret_cast<__nv_bfloat16*>(smem_raw + (CA == 2 ? 16 : 0));
  float*         sR = reinterpret_cast<float*>(smem_raw);
  const uint32_t bar0 = (uint32_t)__cvta_generic_to_shared(smem_raw);

  const int tid  = threadIdx.x;
  const int warp = tid >> 5;
  const int lane = tid & 31;
  const int wn   = warp % WN;
  const int kid  = warp / WN;
  const int c4   = lane & 3;
  const int r8   = lane >> 2;

  const int m0  = blockIdx.x * BM;
  const int n0  = blockIdx.y * BN;
  const int nw0 = n0 + wn * NCOLS;

  const int arows = min(BM, M - m0);
  const int KCS   = K / KCH;               // k per chunk
  const int PITCH = KCS + 8;               // +8 halves -> conflict-free LDS.32
  const int GPKC  = (KCS >> 7) / KS;       // 128-groups per warp per chunk
  const int VPR   = KCS >> 3;              // uint4 per row per chunk
  const int ASZ   = CA ? arows * PITCH : 0;  // stride to the 2nd A buffer (halves)

  float acc[MT][NTILES][4];                // SB: grand total, else per-group
  float tot[MT][NTILES][4];                // SB==0 only (else dead)
#pragma unroll
  for (int a = 0; a < MT; ++a)
#pragma unroll
    for (int b = 0; b < NTILES; ++b)
#pragma unroll
      for (int c = 0; c < 4; ++c) { acc[a][b][c] = 0.f; tot[a][b][c] = 0.f; }

  // Rows >= arows clamp onto the last real row: they compute duplicate garbage
  // that the epilogue drops, which is cheaper than zero-padding shared memory.
  const __nv_bfloat16* pA[MT][2];
#pragma unroll
  for (int mt = 0; mt < MT; ++mt) {
    pA[mt][0] = sA + min(mt * 16 + r8,     arows - 1) * PITCH + c4 * 2;
    pA[mt][1] = sA + min(mt * 16 + r8 + 8, arows - 1) * PITCH + c4 * 2;
  }

  const uint32_t* bpw  = bp + ((size_t)(nw0 >> 5) * (K >> 5) * 32 + lane) * 4;
  const size_t bstride = (size_t)(K >> 5) * 32 * 4;      // next 32-column block
  const uint64_t pol   = pol_evict_first();

  // CA: chunk c of A is copied by cp.async into buffer c&1 one whole chunk
  // early, so the 8-9us the blocking copy costs at M=32 hides under the mmas.
  // One commit_group per chunk (empty when there is nothing left to fetch) keeps
  // the group count exact, so a single wait_group 1 always means "chunk c has
  // landed, chunk c+1 may still be flying".
  const uint32_t asmem = (uint32_t)__cvta_generic_to_shared(sA);
  // CA==2: a chunk's row is KCS contiguous halves in x, so one cp.async.bulk
  // moves it whole.  The per-thread form issues arows*VPR 16B copies per chunk
  // -- 4096 of them at M=32/KCH=4 -- against a ceiling of about one per cycle
  // per SM, which is ~26us of pure issue on a 54us kernel.  The bulk form
  // issues arows, and thread 0 alone hands them to the DMA engine, so no other
  // warp spends an issue slot on staging at all.
  auto stageA = [&](int c) {
    if (CA == 2) {
      if (c < KCH && tid == 0) {
        const uint32_t bar = bar0 + (uint32_t)((c & 1) * 8);
        mbexpect(bar, (uint32_t)(arows * KCS * 2));
        const uint32_t d0 = asmem + (uint32_t)((c & 1) * ASZ * 2);
        for (int r = 0; r < arows; ++r)
          bulkcp(d0 + (uint32_t)(r * PITCH * 2),
                 x + (size_t)(m0 + r) * K + (size_t)c * KCS,
                 (uint32_t)(KCS * 2), bar);
      }
      return;
    }
    if (c < KCH) {
      const uint32_t d0 = asmem + (uint32_t)((c & 1) * ASZ * 2);
      for (int r = warp; r < arows; r += NWRP) {
        const uint4* src = reinterpret_cast<const uint4*>(
            x + (size_t)(m0 + r) * K + (size_t)c * KCS);
        const uint32_t dr = d0 + (uint32_t)(r * PITCH * 2);
        for (int v = lane; v < VPR; v += 32) cpa16ca(dr + (uint32_t)(v * 16), src + v);
      }
    }
    cpcommit();
  };
  if (CA == 2 && tid == 0) { mbinit(bar0, 1); mbinit(bar0 + 8, 1); }
  if (CA == 2) __syncthreads();
  if (CA) stageA(0);

  uint4 br[PD][NW];
  for (int c = 0; c < KCH; ++c) {
    if (!CA && KCH > 1 && c > 0) __syncthreads();        // previous chunk read

    // Prime the B pipeline before staging A so these slices fly during the copy.
    // Under CA only chunk 0 primes: the steady-state prefetch below crosses the
    // chunk boundary there, so the stream is never cold again.  With the
    // blocking A copy the same trick measured slower (the boundary LDGs fight
    // the copy for LSU slots), so that path re-primes per chunk.
    const uint32_t* bqc = bpw + (size_t)(c * (KCS >> 5) + kid * GPKC * 4) * 128;
    const int SLC = GPKC * 4;                            // 32-k slices per chunk
    if (c == 0 || !CA) {
#pragma unroll
      for (int d = 0; d < PD; ++d)
#pragma unroll
        for (int t = 0; t < NW; ++t)
          br[d][t] = ldgb<EV>(bqc + (size_t)d * 128 + t * bstride, pol);
    }

    // One warp per row: every thread stays busy and each lane has VPR/32 loads
    // in flight.  Striding the block over one row at a time instead left half
    // the threads idle and serialised one HBM round trip per row -- 30% of the
    // whole kernel at M=16.
    // Buffer b is filled by chunks b, b+2, ..., so chunk c waits for the
    // (c/2)'th completion of barrier c&1, i.e. phase (c>>1)&1.  Staging c+1
    // goes after the barrier here, not before it as on the cp.async path: it
    // overwrites the buffer chunk c-1 was reading, and only __syncthreads
    // proves every warp is done with it.
    if (CA == 2) {
      mbwait(bar0 + (uint32_t)((c & 1) * 8), (c >> 1) & 1);
      __syncthreads();
      stageA(c + 1);
    } else if (CA) {
      stageA(c + 1);
      cpwait<1>();
      __syncthreads();
    } else {
      // Single-buffered, but still cp.async: a plain dst[v] = src[v] round-trips
      // every 16B through a register, so the STS cannot issue until the LDG
      // retires and the whole copy serialises on L2 latency.  cp.async hands the
      // address pair to the LSU and moves on -- 3.5us of 55 at M=32, 1.0 of 49 at
      // M=16, and 25us at KCH=8 where the copy is twice as long.  .ca not .cg:
      // every n-block reads the same x rows, so the L1 line has to stay.
      for (int r = warp; r < arows; r += NWRP) {
        const uint4* src = reinterpret_cast<const uint4*>(
            x + (size_t)(m0 + r) * K + (size_t)c * KCS);
        const uint32_t dr = asmem + (uint32_t)(r * PITCH * 2);
        for (int v = lane; v < VPR; v += 32) cpa16ca(dr + (uint32_t)(v * 16), src + v);
      }
      cpcommit();
      cpwait<0>();
      __syncthreads();
    }

    const int kb0 = kid * GPKC * 128;
    const int g0  = c * (KCS >> 7) + kid * GPKC;

#pragma unroll 1
    for (int gg = 0; gg < GPKC; ++gg) {
      const int sl0 = gg * 4;                            // first slice of the group
      const __nv_bfloat16* zg = zp + (size_t)(g0 + gg) * N + nw0;
      const __nv_bfloat16* sg = sc + (size_t)(g0 + gg) * N + nw0;
      uint32_t zpx2[NTILES], scx2[NTILES];
      float s0[NTILES], s1[NTILES];
#pragma unroll
      for (int t = 0; t < NTILES; ++t) {
        zpx2[t] = bcast16(zg + t * 8 + r8);
        if (SB) {
          scx2[t] = bcast16(sg + t * 8 + r8);
        } else {
          const uint32_t s2 = *reinterpret_cast<const uint32_t*>(sg + t * 8 + c4 * 2);
          s0[t] = bf2f(s2 & 0xFFFFu);
          s1[t] = bf2f(s2 >> 16);
        }
      }

      if (!SB) {
#pragma unroll
        for (int a = 0; a < MT; ++a)
#pragma unroll
          for (int b = 0; b < NTILES; ++b)
#pragma unroll
            for (int cc = 0; cc < 4; ++cc) acc[a][b][cc] = 0.f;
      }

      const int kb = kb0 + gg * 128;
#pragma unroll
      for (int j = 0; j < 4; ++j) {
        // Consume slice sl0+j, then immediately issue the load PD slices ahead --
        // which crosses into the next group, so the stream never drains.  PD
        // divides 4 so the rotation index stays a compile-time constant and br
        // stays in registers.
        uint4 bw[NW];
#pragma unroll
        for (int t = 0; t < NW; ++t) bw[t] = br[j % PD][t];
        const int sn = sl0 + j + PD;
        if (sn < SLC || (CA && c + 1 < KCH)) {
          // Past the end of the chunk, the next chunk's slice sn-SLC sits
          // SLC*KS slices on from this chunk's slice 0, so one addend keeps the
          // stream running across the boundary as well.  SLC is a multiple of PD,
          // so the rotation slot stays consistent from chunk to chunk.
          const int so = (sn < SLC) ? sn : sn + SLC * (KS - 1);
#pragma unroll
          for (int t = 0; t < NW; ++t)
            br[j % PD][t] = ldgb<EV>(bqc + (size_t)so * 128 + t * bstride, pol);
        }
#pragma unroll
        for (int ks = 0; ks < 2; ++ks) {
          const int kloc = kb + j * 32 + ks * 16;
          uint32_t af[MT][4];
#pragma unroll
          for (int mt = 0; mt < MT; ++mt) {
            af[mt][0] = lds32(pA[mt][0] + kloc);
            af[mt][2] = lds32(pA[mt][0] + kloc + 8);
            af[mt][1] = (AR == 2) ? lds32(pA[mt][1] + kloc)     : 0u;
            af[mt][3] = (AR == 2) ? lds32(pA[mt][1] + kloc + 8) : 0u;
          }
#pragma unroll
          for (int t = 0; t < NW; ++t) {
            const uint32_t w0 = (ks == 0) ? bw[t].x : bw[t].z;
            const uint32_t w1 = (ks == 0) ? bw[t].y : bw[t].w;
#pragma unroll
            for (int i = 0; i < 4; ++i) {
              const int nt = t * 4 + i;
              uint32_t bb[2];
              bb[0] = bsub(dq(w0, 4 * i), zpx2[nt]);
              bb[1] = bsub(dq(w1, 4 * i), zpx2[nt]);
              if (SB) {
                bb[0] = bmul(bb[0], scx2[nt]);
                bb[1] = bmul(bb[1], scx2[nt]);
              }
#pragma unroll
              for (int mt = 0; mt < MT; ++mt) mma16816(acc[mt][nt], af[mt], bb);
            }
          }
        }
      }

      if (!SB) {
#pragma unroll
        for (int mt = 0; mt < MT; ++mt)
#pragma unroll
          for (int t = 0; t < NTILES; ++t) {
            tot[mt][t][0] = fmaf(s0[t], acc[mt][t][0], tot[mt][t][0]);
            tot[mt][t][1] = fmaf(s1[t], acc[mt][t][1], tot[mt][t][1]);
            tot[mt][t][2] = fmaf(s0[t], acc[mt][t][2], tot[mt][t][2]);
            tot[mt][t][3] = fmaf(s1[t], acc[mt][t][3], tot[mt][t][3]);
          }
      }
    }

    if (CA && KCH > 1) {
      __syncthreads();                     // buffer c&1 gets refilled at c+2
      const int dlt = (c & 1) ? -ASZ : ASZ;
#pragma unroll
      for (int mt = 0; mt < MT; ++mt) { pA[mt][0] += dlt; pA[mt][1] += dlt; }
    }
  }

  // ------------------------------------------------------------ epilogue ----
  // Each k-split warp group owns a private NEL-float slab, so the cross-warp
  // reduction is plain stores plus a strided sum.  Accumulating into one shared
  // slab with atomicAdd instead cost 24us of a 62us kernel at M=16: the eight
  // r8 rows of a warp all land in the same banks (BN is a multiple of 32), so
  // every atomic replayed eight ways on top of the read-modify-write.
  const int NEL = arows * BN;
  __syncthreads();                                     // sA reads all done
  float* sK = sR + kid * NEL;

#pragma unroll
  for (int mt = 0; mt < MT; ++mt)
#pragma unroll
    for (int t = 0; t < NTILES; ++t) {
      const int col = wn * NCOLS + t * 8 + c4 * 2;
      const int row = mt * 16 + r8;
      if (row < arows) {
        sK[row * BN + col]     = SB ? acc[mt][t][0] : tot[mt][t][0];
        sK[row * BN + col + 1] = SB ? acc[mt][t][1] : tot[mt][t][1];
      }
      if (AR == 2 && row + 8 < arows) {
        sK[(row + 8) * BN + col]     = SB ? acc[mt][t][2] : tot[mt][t][2];
        sK[(row + 8) * BN + col + 1] = SB ? acc[mt][t][3] : tot[mt][t][3];
      }
    }
  __syncthreads();

  for (int i = tid; i < NEL; i += NTHR) {
    float s = sR[i];
#pragma unroll 4
    for (int q = 1; q < KS; ++q) s += sR[q * NEL + i];
    const int row = i / BN;                            // BN is a power of two
    const int col = i - row * BN;
    out[(size_t)(m0 + row) * N + n0 + col] = __float2bfloat16(s);
  }
}

// ------------------------------------------------------------------ launch ---
// --------------------------------------------------------------------------- //
// big-M kernel: warps tile both m and n, A double-buffered through smem
// --------------------------------------------------------------------------- //
// Above ~M=32 the split-K kernel loses: with BM<=32 it re-reads the whole weight
// matrix M/BM times, and the k-split forces a cross-warp reduction.  Here one
// block owns a BM x BN tile of C, warps tile it WM x WN ways, A is staged one
// 128-k group at a time with cp.async and read with ldmatrix, and B stays in
// registers PD slices deep so the HBM latency of the next slice overlaps the
// mma of the current one.
//
// BM : rows per block        NW : 32-column groups per warp (NT = 4*NW)
// WM : warps along m         WN : warps along n            (BN = 32*NW*WN)
// PD : B slices in flight per warp (must divide 4)
template<int BM, int NW, int WM, int WN, int PD>
__global__ __launch_bounds__(32 * WM * WN) void w4a16_bigm(
    const uint32_t* __restrict__ bp,
    const __nv_bfloat16* __restrict__ x,
    const __nv_bfloat16* __restrict__ sc,
    const __nv_bfloat16* __restrict__ zp,
    __nv_bfloat16* __restrict__ out,
    int M, int N, int K)
{
  constexpr int NTHR  = 32 * WM * WN;
  constexpr int MT    = BM / (16 * WM);
  constexpr int NT    = 4 * NW;
  constexpr int BN    = 32 * NW * WN;
  constexpr int PITCH = 136;                 // 128 k + 8: conflict-free ldmatrix
  constexpr int ASZ   = BM * PITCH;          // bf16 per A buffer
  constexpr int RPP   = NTHR / 16;           // A rows staged per pass
  constexpr int NPASS = (BM + RPP - 1) / RPP;

  extern __shared__ __align__(16) char smem_raw[];
  __nv_bfloat16* sA = reinterpret_cast<__nv_bfloat16*>(smem_raw);

  const int tid = threadIdx.x, warp = tid >> 5, lane = tid & 31;
  const int wm = warp / WN, wn = warp % WN;
  const int c4 = lane & 3, r8 = lane >> 2;
  const int m0 = blockIdx.x * BM, n0 = blockIdx.y * BN;
  const int arows = min(BM, M - m0);
  const int NG = K >> 7;                     // 128-k groups
  const int NSL = K >> 5;                    // 32-k slices

  // A staging: thread tid moves 16 B of row (tid>>4) per pass.  The addresses
  // are rebuilt from two bases rather than kept in arrays -- at 512 threads the
  // whole kernel has to fit in 128 registers or the inner loop spills.
  const int srow = tid >> 4, scol = (tid & 15) << 3;
  const __nv_bfloat16* xb = x + (size_t)m0 * K + scol;
  const uint32_t dbase = (uint32_t)__cvta_generic_to_shared(sA + scol);
#define STAGE_A(GRP, BUFOFF)                                                   \
  {                                                                            \
    _Pragma("unroll")                                                          \
    for (int p = 0; p < NPASS; ++p) {                                          \
      const int r = srow + p * RPP;                                            \
      if (r < BM)                                                              \
        cpa16(dbase + (uint32_t)(r * PITCH * 2) + (uint32_t)(BUFOFF),           \
              xb + (size_t)min(r, arows - 1) * K + 128 * (GRP));                \
    }                                                                          \
  }

  uint32_t aad[MT];
#pragma unroll
  for (int mt = 0; mt < MT; ++mt) {
    const int row = min(wm * MT * 16 + mt * 16 + (lane & 15), arows - 1);
    aad[mt] = (uint32_t)__cvta_generic_to_shared(sA + row * PITCH + ((lane >> 4) << 3));
  }

  const size_t bstride = (size_t)NSL * 128;
  const uint32_t* bq = bp + (size_t)((n0 + wn * 32 * NW) >> 5) * bstride + lane * 4;
  const __nv_bfloat16* zg = zp + n0 + wn * 32 * NW + r8;
  const __nv_bfloat16* sg = sc + n0 + wn * 32 * NW + r8;

  float acc[MT][NT][4];
#pragma unroll
  for (int a = 0; a < MT; ++a)
#pragma unroll
    for (int b = 0; b < NT; ++b)
#pragma unroll
      for (int c = 0; c < 4; ++c) acc[a][b][c] = 0.f;

  uint4 br[PD][NW];
#pragma unroll
  for (int d = 0; d < PD; ++d)
#pragma unroll
    for (int t = 0; t < NW; ++t)
      br[d][t] = *reinterpret_cast<const uint4*>(bq + (size_t)d * 128 + t * bstride);

  STAGE_A(0, 0)
  cpcommit();

#pragma unroll 1
  for (int g = 0; g < NG; ++g) {
    const int buf = g & 1;
    cpwait<0>();
    __syncthreads();
    if (g + 1 < NG) STAGE_A(g + 1, (buf ^ 1) * ASZ * 2)
    cpcommit();

    uint32_t zpx2[NT], scx2[NT];
#pragma unroll
    for (int t = 0; t < NT; ++t) {
      zpx2[t] = bcast16(zg + (size_t)g * N + t * 8);
      scx2[t] = bcast16(sg + (size_t)g * N + t * 8);
    }
    const uint32_t abase = (uint32_t)(buf * ASZ * 2);

#pragma unroll
    for (int j = 0; j < 4; ++j) {
      uint4 w[NW];
#pragma unroll
      for (int t = 0; t < NW; ++t) w[t] = br[j % PD][t];
      const int snx = g * 4 + j + PD;
      if (snx < NSL) {
#pragma unroll
        for (int t = 0; t < NW; ++t)
          br[j % PD][t] = *reinterpret_cast<const uint4*>(bq + (size_t)snx * 128 + t * bstride);
      }
#pragma unroll
      for (int ks = 0; ks < 2; ++ks) {
        uint32_t af[MT][4];
#pragma unroll
        for (int mt = 0; mt < MT; ++mt)
          ldm4(af[mt], aad[mt] + abase + (uint32_t)((j * 32 + ks * 16) * 2));
#pragma unroll
        for (int t = 0; t < NW; ++t) {
          const uint32_t w0 = (ks == 0) ? w[t].x : w[t].z;
          const uint32_t w1 = (ks == 0) ? w[t].y : w[t].w;
#pragma unroll
          for (int i = 0; i < 4; ++i) {
            const int nt = t * 4 + i;
            uint32_t bb[2];
            bb[0] = bmul(bsub(dq(w0, 4 * i), zpx2[nt]), scx2[nt]);
            bb[1] = bmul(bsub(dq(w1, 4 * i), zpx2[nt]), scx2[nt]);
#pragma unroll
            for (int mt = 0; mt < MT; ++mt) mma16816(acc[mt][nt], af[mt], bb);
          }
        }
      }
    }
  }

#pragma unroll
  for (int mt = 0; mt < MT; ++mt) {
    const int row = wm * MT * 16 + mt * 16 + r8;
#pragma unroll
    for (int t = 0; t < NT; ++t) {
      const int col = n0 + wn * 32 * NW + t * 8 + c4 * 2;
      if (row < arows)
        *reinterpret_cast<uint32_t*>(out + (size_t)(m0 + row) * N + col) =
            pk2(acc[mt][t][0], acc[mt][t][1]);
      if (row + 8 < arows)
        *reinterpret_cast<uint32_t*>(out + (size_t)(m0 + row + 8) * N + col) =
            pk2(acc[mt][t][2], acc[mt][t][3]);
    }
  }
#undef STAGE_A
}

//     ID  BM  NW  WM  WN  PD
#define BIGM_LIST                       \
  Z( 0, 128,  1,  2,  8,  2)            \
  Z( 1, 128,  2,  4,  4,  2)            \
  Z( 2, 128,  1,  4,  4,  4)            \
  Z( 3, 128,  2,  4,  4,  1)            \
  Z( 4, 128,  1,  4,  4,  2)            \
  Z( 5,  64,  1,  1,  8,  2)            \
  Z( 6,  64,  1,  2,  8,  4)            \
  Z( 7,  64,  2,  2,  8,  2)            \
  Z( 8, 256,  1,  4,  4,  2)            \
  Z( 9,  32,  1,  1,  4,  4)            \
  Z(10,  32,  1,  2,  8,  2)            \
  Z(11,  64,  1,  4,  4,  4)            \
  Z(12,  64,  1,  1,  4,  2)            \
  Z(13,  64,  1,  1,  4,  4)            \
  Z(14, 128,  1,  1,  4,  2)            \
  Z(15, 128,  1,  2,  4,  2)            \
  Z(16,  64,  1,  1,  2,  2)            \
  Z(17, 128,  1,  1,  2,  2)            \
  Z(18,  64,  2,  1,  4,  2)            \
  Z(19, 256,  1,  2,  4,  2)            \
  Z(20, 128,  1,  1,  8,  2)            \
  Z(21, 256,  1,  1,  4,  2)            \
  Z(22, 128,  2,  2,  4,  2)            \
  Z(23,  64,  1,  1,  8,  4)            \
  Z(24,  32,  1,  1,  2,  4)            \
  Z(25,  32,  1,  1,  8,  2)

#define BIGM_NCFG 26
#define BIGM_BASE 1000

template<int BM, int NW, int WM, int WN, int PD>
static void run_bigm(const void* bp, const void* x, const void* sc, const void* zp,
                     void* out, int M, int N, int K, cudaStream_t st) {
  constexpr int BN   = 32 * NW * WN;
  constexpr int NTHR = 32 * WM * WN;
  constexpr int smem = 2 * BM * 136 * 2;
  static bool set = false;
  if (smem > 48 * 1024 && !set) {
    cudaFuncSetAttribute((const void*)w4a16_bigm<BM, NW, WM, WN, PD>,
                         cudaFuncAttributeMaxDynamicSharedMemorySize, smem);
    set = true;
  }
  dim3 grid((M + BM - 1) / BM, N / BN);
  w4a16_bigm<BM, NW, WM, WN, PD><<<grid, NTHR, smem, st>>>(
      (const uint32_t*)bp, (const __nv_bfloat16*)x, (const __nv_bfloat16*)sc,
      (const __nv_bfloat16*)zp, (__nv_bfloat16*)out, M, N, K);
}

// ========================================================================= //
// warpgroup path: weights in registers, activations in shared memory
// ========================================================================= //
// Every kernel above keeps activations in registers and weights in shared
// memory, so the dequantized bf16 weights have to round-trip through smem --
// 25MB of stores and matching loads that the int4 format existed to avoid.
// wgmma's RS form reads its A operand from registers, so handing it the
// *weights* leaves the unpacked values where they were produced and lets smem
// hold only the (far smaller) activation tile.  The price is that the operand
// roles are transposed: wgmma's "M" is a block of 64 weight columns and its
// "N" is MN activation rows, so the weights need their own ALayout_64x16
// packing (_repack_wg) and the accumulator comes out as a column strip per
// thread, which the epilogue bounces through smem to get coalesced rows.
//
// The other half of making it pay is staging.  Walking x in row-major order
// makes a warp cover 8 rows x 64B, and every row-strided global->smem mapping
// measured on this card tops out at one 16B cp.async per cycle per SM: 2048
// cycles per 64-k chunk against 1032 cycles of wgmma, so the tensor cores
// starve and the whole transposition buys nothing.  So x is pre-permuted into
// descriptor order by w4a16_xreorder -- a separate 4MB pass over activations
// only, the int4 unpack stays fused in the mainloop -- and a chunk becomes one
// flat 32KB memcpy, 512B contiguous in and 512B contiguous out per warp.
//
// Measured against the mma.sync path: 2.0x at M=256 (185.5us -> 92us), a wash
// at M=32 and a loss below it, because a small activation tile cannot amortize
// its own staging.  _wg_candidates gates on M accordingly.

#define WG_BASE 3000

// smem matrix descriptor: addr(13:0) lbo(29:16) sbo(45:32) base(51:49) lay(63:62)
DEVI uint64_t mkdesc(uint32_t sa, uint32_t lbo, uint32_t sbo) {
  return ((uint64_t)((sa >> 4) & 0x3FFFu))
       | ((uint64_t)((lbo >> 4) & 0x3FFFu) << 16)
       | ((uint64_t)((sbo >> 4) & 0x3FFFu) << 32);
}
DEVI void wgfence() { asm volatile("wgmma.fence.sync.aligned;\n" ::: "memory"); }
DEVI void wgcommit() { asm volatile("wgmma.commit_group.sync.aligned;\n" ::: "memory"); }
template<int G> DEVI void wgwait() {
  asm volatile("wgmma.wait_group.sync.aligned %0;\n" :: "n"(G) : "memory");
}

// One wgmma.mma_async per MN, generated by _wgmma_ptx -- the accumulator
// operand list has MN/2 entries and has to be written out literally.
template<int MN> DEVI void wgmmaN(float*, const uint32_t*, uint64_t);
//@WGMMA@

// x (M,K) bf16 row-major -> the byte order a chunk's flat memcpy has to
// produce.  A unit is 16B = 8 consecutive k of one row, and the flat order,
// outermost first, is chunk (k/64), ktile ((k%64)/16), n/8, slot ((k%16)/8),
// n%8 -- so one block owns one (chunk, ktile) pair and writes its MN*32 bytes
// out contiguously.  The reads are a gather (a warp covers 16 rows x 32B), but
// the three sibling ktiles of a chunk are read by sibling blocks and hit in
// L2, so DRAM still sees each line once.  Rows past M are zero-filled.
__global__ __launch_bounds__(256) void w4a16_xreorder(
    const uint4* __restrict__ x, uint4* __restrict__ xr, int M, int MN, int K)
{
  const int kt = blockIdx.x & 3, chunk = blockIdx.x >> 2;
  const int ku = (chunk * 64 + kt * 16) >> 3;   // 16B unit index along k
  const int KU = K >> 3;                        // units per row
  uint4* d = xr + (size_t)blockIdx.x * (MN * 2);
  for (int s = threadIdx.x; s < MN * 2; s += blockDim.x) {
    const int n = (s >> 4) * 8 + (s & 7), slot = (s >> 3) & 1;
    d[s] = (n < M) ? x[(size_t)n * KU + ku + slot] : make_uint4(0, 0, 0, 0);
  }
}

// WG warpgroups, each owning 64 weight columns; MN activation rows per wgmma;
// 64 k per chunk (= one uint4 of packed weights per thread), NBUF chunks
// resident so a chunk can be staged while an earlier one is still being read
// by an in-flight wgmma; PD uint4 of weights in flight per thread; TM stages
// with cp.async.bulk instead of per-thread cp.async.
template<int WG, int MN, int PD, int TM, int ZS, int NB, int SY>
__global__ __launch_bounds__(128 * WG) void w4a16_wg(
    const uint4* __restrict__ bw, const __nv_bfloat16* __restrict__ x,
    const __nv_bfloat16* __restrict__ sc, const __nv_bfloat16* __restrict__ zp,
    __nv_bfloat16* __restrict__ out, int M, int N, int K)
{
  constexpr int NTHR = 128 * WG;
  constexpr int NACC = MN / 2;
  constexpr int TILE = MN * 32;              // bytes: MN rows x 16k x 2B
  constexpr int KC   = 64;                   // k per chunk
  constexpr int CHB  = 4 * TILE;             // bytes per chunk (4 k-tiles)
  // A chunk lands in wr[p%PD] and is read PD chunks later at wr[(p+PD)%UF %PD],
  // so the two agree only when UF % PD == 0.  PD=8 against UF=4 aliases four of
  // the eight slots and computes garbage -- and measured FASTER than the correct
  // configs (56.0us vs 57.3), which _autotune ranks on time alone would have
  // selected.  Widening the unroll with PD keeps the index static, which is the
  // whole reason it is p%PD and not ch%PD: ch%PD varies with cb and would push
  // wr to local memory.
  constexpr int NBUF = NB, UF = PD > 4 ? 8 : 4;
  static_assert(UF % PD == 0, "wr slot p%PD must match the chunk's own slot");
  // SY is the __syncthreads period in chunks.  The sync is not there for the
  // data -- the mbarrier covers that -- but to stop tid 0, the only thread that
  // arms and issues, from lapping the others and re-arming a barrier they have
  // not waited on yet.  Buffer b holds chunk c, is read by a wgmma issued at
  // iteration c that only retires at the bottom of iteration c+1, and is
  // re-armed for chunk c+NBUF at iteration c+NBUF-2.  tid 0 reaching that has
  // passed a sync at >= c+NBUF-1-SY, which drags every thread to that
  // iteration, so c+NBUF-1-SY > c+1 -- NBUF >= SY+3.  SY=1 makes that 4, which
  // is exactly the shipped NBUF, and is why dropping the sync outright hangs.
  static_assert(NBUF >= SY + 3, "a re-armed buffer must outlive its wgmma");
  static_assert(UF % SY == 0, "the sync must land on the same p every block");
  constexpr int BN   = 64 * WG;
  constexpr int OPIT = BN + 8;               // epilogue pitch, in bf16
  constexpr int NCP  = CHB / (NTHR * 16);    // 16B copies per thread per chunk

  extern __shared__ __align__(16) char sraw[];
  const uint32_t sb0 = (uint32_t)__cvta_generic_to_shared(sraw);
  __nv_bfloat16* sO = reinterpret_cast<__nv_bfloat16*>(sraw);
  // NBUF mbarriers parked past the staging buffers; the epilogue's sO is
  // always the smaller of the two smem claims, so it never reaches them.
  const uint32_t bar0 = sb0 + (uint32_t)(NBUF * CHB);
  // ZS parks the whole block's {zero,scale} table past the mbarriers: K/128
  // groups x BN columns x one uint32, 16KB at BN=128/K=4096, read by LDS
  // instead of L1.  NBUF*CHB + NBUF*8 keeps this 16B-aligned for the v4 store.
  const uint32_t zsb = bar0 + (uint32_t)(NBUF * 8);

  const int tid = threadIdx.x, wg = tid >> 7, t = tid & 127;
  const int c4 = t & 3, base = ((t >> 2) & 7) + 16 * (t >> 5);
  const int n0 = (blockIdx.x * WG + wg) * 64;

  float acc[NACC];
#pragma unroll
  for (int i = 0; i < NACC; ++i) acc[i] = 0.f;

  const uint4* wp = bw + (size_t)(blockIdx.x * WG + wg) * (K / 64) * 128 + t;
  const __nv_bfloat16* zg = zp + n0 + base;
  const __nv_bfloat16* sg = sc + n0 + base;

#define WGSTAGE(CH, BUF)                                                      \
  {                                                                           \
    if (TM) {                                                                 \
      if (tid == 0) {                                                         \
        mbexpect(bar0 + (uint32_t)((BUF) * 8), CHB);                          \
        bulkcp(sb0 + (uint32_t)((BUF) * CHB),                                 \
               (const char*)x + (size_t)(CH) * CHB, CHB,                      \
               bar0 + (uint32_t)((BUF) * 8));                                 \
      }                                                                       \
    } else {                                                                  \
      const uint32_t d0 = sb0 + (uint32_t)((BUF) * CHB) + (uint32_t)(tid << 4); \
      const char* s0_ = (const char*)x + (size_t)(CH) * CHB + (tid << 4);     \
      _Pragma("unroll")                                                       \
      for (int u = 0; u < NCP; ++u)                                           \
        cpa16(d0 + (uint32_t)(u * NTHR * 16), s0_ + u * NTHR * 16);           \
      cpcommit();                                                             \
    }                                                                         \
  }
  // Chunk CH lands in buffer CH%NBUF, which has completed CH/NBUF times
  // before, so the phase bit it is waiting to flip to is (CH/NBUF)&1.
#define WGWAIT(CH)                                                            \
  { if (TM) mbwait(bar0 + (uint32_t)(((CH) % NBUF) * 8), ((CH) / NBUF) & 1);  \
    else    cpwait<1>(); }

  if (TM) {
    if (tid == 0)
#pragma unroll
      for (int b = 0; b < NBUF; ++b) mbinit(bar0 + (uint32_t)(b * 8), 1);
    __syncthreads();
  }

  const int NQ = K / KC;
  uint4 wr[PD];
#pragma unroll
  for (int d = 0; d < PD; ++d) wr[d] = wp[(size_t)d * 128];

  WGSTAGE(0, 0)
  WGSTAGE(1, 1)
  uint32_t afb[2][4][4];

  // The inline zero/scale loads are not latency-bound -- the SASS already sinks
  // them two chunks ahead and prefetching them into registers bought only 1.7us
  // of the 8.4 they cost.  They are L1 *wavefront* count: a warp's LDG.U16
  // covers 8 distinct halves in 16B, one wavefront that moves 16 of the 25MB
  // the block streams, and there are 128 of them per thread against 64 weight
  // LDG.128.  Two thirds of the L1 traffic for a thirtieth of the bytes.
  // Staging the table once, as {zero,scale} packed into one uint32 per column,
  // turns all of it into conflict-free LDS.  Worth 3.8% on shape2 by a
  // 201-round interleaved A/B (scratch/ab.py); the wg4 scratch harness read
  // it as 0.9% because it hands the kernel a pre-permuted x and so enters
  // with a different L2 state than the real xreorder->gemm pair.
  const int NGR = K >> 7;                      // 128-k groups
  const uint32_t zsl = zsb + (uint32_t)((wg * 64 + base) * 4);
  if (ZS) {
    // 8 columns per uint4, so 2 iterations per thread at BN=128/NTHR=256.
    for (int i = tid; i < NGR * (BN / 8); i += NTHR) {
      const int g = i / (BN / 8), n8 = (i - g * (BN / 8)) * 8;
      const size_t o = (size_t)g * N + (size_t)blockIdx.x * BN + n8;
      const uint4 zv = *reinterpret_cast<const uint4*>(zp + o);
      const uint4 sv = *reinterpret_cast<const uint4*>(sc + o);
      const uint32_t da = zsb + (uint32_t)((g * BN + n8) * 4);
      const uint32_t zw[4] = {zv.x, zv.y, zv.z, zv.w};
      const uint32_t sw[4] = {sv.x, sv.y, sv.z, sv.w};
      uint32_t d[8];
#pragma unroll
      for (int q = 0; q < 4; ++q) {
        d[2 * q]     = (zw[q] & 0xffffu) | (sw[q] << 16);
        d[2 * q + 1] = (zw[q] >> 16)     | (sw[q] & 0xffff0000u);
      }
      asm volatile("st.shared.v4.b32 [%0], {%1,%2,%3,%4};" :: "r"(da),
                   "r"(d[0]), "r"(d[1]), "r"(d[2]), "r"(d[3]) : "memory");
      asm volatile("st.shared.v4.b32 [%0], {%1,%2,%3,%4};" :: "r"(da + 16),
                   "r"(d[4]), "r"(d[5]), "r"(d[6]), "r"(d[7]) : "memory");
    }
    __syncthreads();
  }

  for (int cb = 0; cb < K / KC / UF; ++cb) {
#pragma unroll
    for (int p = 0; p < UF; ++p) {
      const int ch = cb * UF + p;
      WGWAIT(ch)                             // chunk ch has landed
      if (p % SY == 0) __syncthreads();
      // Buffer (ch+2)%NBUF is two chunks ahead of the one an in-flight wgmma
      // is still reading, so staging into it now cannot race.
      if (ch + 2 < NQ) WGSTAGE(ch + 2, (ch + 2) % NBUF)
      else if (!TM) cpcommit();

      const uint64_t db = mkdesc(sb0 + (uint32_t)((ch % NBUF) * CHB), 128, 256);
      const uint4 w = wr[p % PD];
      wr[p % PD] = wp[(size_t)(ch + PD < NQ ? ch + PD : NQ - 1) * 128];
      const int gr = ch >> 1;                // 128-k group
      uint32_t z0, z1, s0, s1;
      if (ZS) {
        // A warp's 8 columns are 8 consecutive uint32 = 8 distinct banks, each
        // read by 4 lanes, so both of these are one conflict-free wavefront.
        const uint32_t a_ = zsl + (uint32_t)(gr * BN * 4);
        uint32_t v0, v1;
        asm("ld.shared.b32 %0, [%1];" : "=r"(v0) : "r"(a_));
        asm("ld.shared.b32 %0, [%1];" : "=r"(v1) : "r"(a_ + 32));
        z0 = prmt2(v0, 0x1010u); s0 = prmt2(v0, 0x3232u);
        z1 = prmt2(v1, 0x1010u); s1 = prmt2(v1, 0x3232u);
      } else {
        z0 = bcast16(zg + (size_t)gr * N);
        z1 = bcast16(zg + (size_t)gr * N + 8);
        s0 = bcast16(sg + (size_t)gr * N);
        s1 = bcast16(sg + (size_t)gr * N + 8);
      }
      const int cur = p & 1;
#pragma unroll
      for (int j = 0; j < 4; ++j) {
        const uint32_t ww = (j == 0) ? w.x : (j == 1) ? w.y : (j == 2) ? w.z : w.w;
        afb[cur][j][0] = bmul(bsub(dq(ww,  0), z0), s0);
        afb[cur][j][1] = bmul(bsub(dq(ww,  4), z1), s1);
        afb[cur][j][2] = bmul(bsub(dq(ww,  8), z0), s0);
        afb[cur][j][3] = bmul(bsub(dq(ww, 12), z1), s1);
      }
      wgfence();
#pragma unroll
      for (int j = 0; j < 4; ++j)
        wgmmaN<MN>(acc, afb[cur][j], db + (uint64_t)(j * (TILE >> 4)));
      wgcommit();
      wgwait<1>();                           // afb[cur^1] is free again
    }
  }
  wgwait<0>();
  if (!TM) cpwait<0>();                      // sO aliases the staging buffers

  // CLayout_64xN: value i -> m = base + 8*((i/2)%2), n = 2*(t%4)+(i%2)+8*(i/4).
  // "m" is a weight column and "n" an activation row, so a thread holds a
  // column strip; bounce through smem to get coalesced rows out.
  __syncthreads();
#pragma unroll
  for (int i = 0; i < NACC; ++i) {
    const int col = wg * 64 + base + 8 * ((i >> 1) & 1);
    const int row = 2 * c4 + (i & 1) + 8 * (i >> 2);
    if (row < M) sO[row * OPIT + col] = __float2bfloat16(acc[i]);
  }
  __syncthreads();
  const int nb0 = blockIdx.x * BN;
  for (int idx = tid; idx < M * (BN / 8); idx += NTHR) {
    const int row = idx / (BN / 8), col = (idx % (BN / 8)) * 8;
    *reinterpret_cast<uint4*>(out + (size_t)row * N + nb0 + col) =
        *reinterpret_cast<const uint4*>(sO + row * OPIT + col);
  }
#undef WGSTAGE
#undef WGWAIT
}

// One scratch buffer for the permuted activations (2MB at MN=256, K=4096),
// shared by every wg launch.  It is allocated on the first launch, which is
// always the eager one _rebind does before capturing, so no allocation ever
// happens inside a graph capture.  The reorder and the gemm are adjacent on
// one stream and launches on that stream are serialized, so sharing is safe.
static void* g_xr = nullptr;
static size_t g_xr_sz = 0;

template<int WG, int MN, int PD, int TM, int ZS, int NB, int SY>
static void run_wg(const void* bp, const void* x, const void* sc, const void* zp,
                   void* out, int M, int N, int K, cudaStream_t st) {
  constexpr int NTHR = 128 * WG, BN = 64 * WG;
  // K is a runtime argument, so the ZS table makes the request runtime too;
  // cudaFuncSetAttribute is asked for the largest K this config will ever see.
  const int sm1  = NB * 4 * (MN * 32) + NB * 8   // + NBUF mbarriers
                 + (ZS ? (K / 128) * BN * 4 : 0);
  constexpr int sm2  = MN * (BN + 8) * 2;
  const int smem = sm1 > sm2 ? sm1 : sm2;
  // A K large enough to overflow smem with the table would need K > 24576, but
  // fall back rather than skip the launch: a config that quietly does nothing
  // reads as the fastest one to _autotune, which ranks on time alone.
  if (ZS && smem > 227 * 1024) {
    run_wg<WG, MN, PD, TM, 0, NB, SY>(bp, x, sc, zp, out, M, N, K, st);
    return;
  }
  const size_t need = (size_t)MN * K * 2;
  if (need > g_xr_sz) {
    if (g_xr) cudaFree(g_xr);
    if (cudaMalloc(&g_xr, need) != cudaSuccess) {
      g_xr = nullptr; g_xr_sz = 0; cudaGetLastError(); return;
    }
    g_xr_sz = need;
  }
  // smem grows with K under ZS, so track the high-water mark rather than a
  // once-only flag: a later, larger K must be able to raise the limit again.
  static int set = 0;
  if (smem > 48 * 1024 && smem > set) {
    cudaFuncSetAttribute((const void*)w4a16_wg<WG, MN, PD, TM, ZS, NB, SY>,
                         cudaFuncAttributeMaxDynamicSharedMemorySize, smem);
    set = smem;
  }
  w4a16_xreorder<<<K / 16, 256, 0, st>>>(
      (const uint4*)x, (uint4*)g_xr, M, MN, K);
  w4a16_wg<WG, MN, PD, TM, ZS, NB, SY><<<N / BN, NTHR, smem, st>>>(
      (const uint4*)bp, (const __nv_bfloat16*)g_xr, (const __nv_bfloat16*)sc,
      (const __nv_bfloat16*)zp, (__nv_bfloat16*)out, M, N, K);
}

// NCP = 8*MN/NTHR must be a positive integer, so MN=16 would need WG=1; WG=4
// with MN=256 would want 128 accumulators inside a 128-register budget.
// ZS stages the {zero,scale} table in smem instead of re-reading it from L1
// every chunk; it is 0.9% on shape2 and costs 16KB of the 227KB budget.
// NB and SY are both measured dead ends, kept as parameters only because the
// generic %NBUF folds to &3 at NB=4 and costs nothing.  NB=6 is exactly neutral
// (+0.10% paired over 151 rounds), and SY=2 -- half the __syncthreads, which
// NB=6 makes legal -- is 1.0% SLOWER.  The barrier is not a cost being paid,
// it is what keeps the warps together so tid 0 issues its TMA on time.
//
// MN is the wgmma's n, so a block's tensor work is MN*64*K whether or not those
// rows are real, and MN=256 at M=32 throws away 7/8 of it.  That reads like a
// reason to build n=32/n=64 wgmma rows -- nothing here depends on MN except
// NACC=MN/2 and the chunk size, and the descriptor's lbo=128/sbo=256 describe
// core-matrix tiling that is identical at every n -- so 16 of them were built
// (WG 1/2, PD 4/8, NB 4/8/16, TM 0/1) and measured.  All correct, all lost, and
// they are gone again because the padding was never the binding constraint:
//
// this path's thread count is 2N no matter how WG is set, since BN=64*WG and
// threads/block=128*WG cancel.  At N=12288 that is 24576 threads against the
// mma path's 49152, and a pure strided read of shape1's 25.2MB while issuing
// this kernel's ~116 instructions per 16B load (scratch/rdwork.py) caps at
// 734 GB/s with 96x256 threads versus 935 at 192x256.  MN=32 measured 635 GB/s
// -- 87% of a ceiling the mma path already beats in absolute terms.  Splitting
// K inside the block to double the threads does not rescue it either: running
// this kernel at 2x and 4x the blocks with K/2 and K/4 (identical traffic and
// per-block work to that split) stays at 660 GB/s.
//
// So only MN=256 survives, for M>=128 where the padding is under 2x.  Small M
// belongs to the mma path, which is why _wg_candidates caps MN at 2*M.
//     ID  WG   MN  PD  TM  ZS  NB  SY
#define WG_LIST                         \
  W( 0,  2, 256,  4,  1,  1,  4,  1)    \
  W( 1,  2, 256,  2,  1,  1,  4,  1)    \
  W( 2,  2, 256,  4,  0,  1,  4,  1)    \
  W( 3,  2, 256,  4,  1,  0,  4,  1)    \
  W( 4,  2, 256,  4,  0,  0,  4,  1)
#define NWGCFG 5

//     ID  BM  NW  WN  KS KCH SB PD EV
#define CFG_LIST                        \
  X( 0, 16,  2,  1,  8,  1, 0, 4, 1)    \
  X( 1, 16,  2,  2,  8,  1, 0, 4, 1)    \
  X( 2, 16,  1,  1,  8,  1, 0, 4, 1)    \
  X( 3, 16,  1,  1, 16,  1, 0, 4, 1)    \
  X( 4, 16,  2,  1, 16,  1, 0, 4, 1)    \
  X( 5, 16,  2,  4,  8,  1, 0, 4, 1)    \
  X( 6, 16,  1,  2,  8,  1, 0, 4, 1)    \
  X( 7, 16,  2,  1,  4,  1, 0, 4, 1)    \
  X( 8, 16,  1,  1, 32,  1, 0, 4, 1)    \
  X( 9, 16,  2,  2,  4,  1, 0, 4, 1)    \
  X(10, 16,  1,  4,  8,  1, 0, 4, 1)    \
  X(11, 16,  2,  1,  8,  2, 0, 4, 1)    \
  X(12, 16,  2,  2,  8,  2, 0, 4, 1)    \
  X(13, 16,  2,  1,  8,  4, 0, 4, 1)    \
  X(14, 16,  2,  2,  8,  4, 0, 4, 1)    \
  X(15, 16,  1,  1,  8,  4, 0, 4, 1)    \
  X(16, 16,  1,  2,  8,  4, 0, 4, 1)    \
  X(17, 16,  2,  1,  4,  4, 0, 4, 1)    \
  X(18, 32,  2,  1,  8,  2, 0, 4, 1)    \
  X(19, 32,  2,  1,  8,  4, 0, 4, 1)    \
  X(20, 32,  2,  2,  8,  4, 0, 4, 1)    \
  X(21, 32,  1,  1,  8,  4, 0, 4, 1)    \
  X(22, 32,  2,  1,  4,  4, 0, 4, 1)    \
  X(23, 32,  2,  2,  4,  4, 0, 4, 1)    \
  X(24, 32,  1,  2,  8,  4, 0, 4, 1)    \
  X(25, 32,  2,  1,  8,  8, 0, 4, 1)    \
  X(26, 32,  2,  2,  8,  8, 0, 4, 1)    \
  X(27, 32,  2,  1,  4,  4, 1, 4, 1)    \
  X(28, 32,  2,  2,  4,  4, 1, 4, 1)    \
  X(29, 64,  1,  1,  4,  8, 1, 4, 1)    \
  X(30, 64,  1,  2,  4,  8, 1, 4, 1)    \
  X(31, 64,  1,  4,  4,  8, 1, 4, 1)    \
  X(32, 64,  1,  2,  4,  4, 1, 4, 1)    \
  X(33, 64,  1,  4,  2,  8, 1, 4, 1)    \
  X(34, 64,  1,  2,  2, 16, 1, 4, 1)    \
  X(35, 32,  2,  1,  2,  8, 1, 4, 1)    \
  X(36, 16,  1,  2,  8,  2, 1, 4, 1)    \
  X(37, 16,  1,  2,  8,  2, 1, 2, 1)    \
  X(38, 16,  1,  4,  8,  2, 1, 4, 1)    \
  X(39, 16,  1,  2, 16,  2, 1, 4, 1)    \
  X(40, 16,  2,  2,  8,  2, 1, 4, 1)    \
  X(41, 16,  1,  2,  8,  4, 1, 4, 1)    \
  X(42, 16,  1,  2,  8,  2, 1, 4, 0)    \
  X(43, 32,  1,  2,  8,  4, 1, 4, 1)    \
  X(44, 32,  1,  2,  8,  4, 1, 4, 0)    \
  X(45, 32,  1,  4,  8,  4, 1, 4, 1)    \
  X(46, 32,  2,  2,  8,  4, 1, 4, 1)    \
  X(47, 32,  1,  2,  8,  8, 1, 4, 1)    \
  X(48, 32,  1,  2,  4,  4, 1, 4, 1)    \
  X(49, 32,  1,  2,  4,  4, 0, 4, 1)    \
  X(50, 32,  1,  4,  4,  4, 0, 4, 1)    \
  X(51, 32,  1,  4,  4,  4, 1, 4, 1)    \
  X(52, 16,  1,  2,  4,  2, 1, 4, 1)    \
  X(53, 16,  1,  2,  4,  4, 1, 4, 1)    \
  X(54, 16,  1,  4,  4,  2, 1, 4, 1)    \
  X(55, 16,  1,  2,  4,  2, 1, 2, 1)

// A block's warp count is 32*WN*KS and its grid is N/(32*NW*WN), so the whole
// launch runs N*KS/(32*NW) warps no matter how WN splits them.  At M=32 every
// tile here has KS=4 and NW=1, which is only 13.5 warps per SM at N=12288 --
// half what M=16 gets -- and an N sweep of the winner shows the cost: 573 GB/s
// at 192 blocks against an 851 GB/s marginal rate once the grid is deep enough
// to fill the machine.  KS=8 would double the warps, and eight such tiles were
// tried (KS=8 with PD<=2 so the prefetch fits inside a 4-slice chunk, plus a
// BN=32 variant to raise the block count directly).  Every one lost: 56.9us for
// the best against 48.3us for cfg 48, and 94.2us for BN=32.  The reason is that
// each N-block stages the whole BM x K activation tile, so halving BN doubles
// the 48MB of x that already flows through L2 -- at M=32 the re-read of x, not
// the warp count, is what sets the rate.
#define NCFG 56

// The same tiles with cp.async double-buffered A (CA=1).  Kept as a separate
// list at CA_BASE so every tuned config above keeps its exact shared-memory
// footprint and occupancy; here the A buffer is doubled, which costs residency,
// so it only pays for the tiles whose staging cost was exposed.
//     ID  BM  NW  WN  KS KCH SB PD EV  CA        CA=2 -> cp.async.bulk
#define CACFG_LIST                          \
  X( 0, 32,  1,  2,  4,  8, 1, 4, 1, 1)    \
  X( 1, 32,  1,  2,  4,  4, 1, 4, 1, 1)    \
  X( 2, 32,  1,  4,  4,  8, 1, 4, 1, 1)    \
  X( 3, 32,  1,  2,  8,  4, 1, 4, 1, 1)    \
  X( 4, 32,  2,  2,  4,  8, 1, 4, 1, 1)    \
  X( 5, 32,  1,  2,  4,  8, 0, 4, 1, 1)    \
  X( 6, 32,  1,  2,  4,  8, 1, 2, 1, 1)    \
  X( 7, 32,  2,  1,  4,  8, 1, 4, 1, 1)    \
  X( 8, 16,  1,  2,  8,  4, 1, 4, 1, 1)    \
  X( 9, 16,  1,  2,  8,  4, 1, 2, 1, 1)    \
  X(10, 16,  1,  2,  4,  8, 1, 4, 1, 1)    \
  X(11, 16,  1,  4,  8,  4, 1, 4, 1, 1)    \
  X(12, 16,  1,  2,  8,  2, 1, 2, 1, 1)    \
  X(13, 16,  1,  4,  4,  4, 1, 4, 1, 1)    \
  X(14, 32,  1,  2,  4,  8, 1, 4, 1, 2)    \
  X(15, 32,  1,  2,  4,  4, 1, 4, 1, 2)    \
  X(16, 32,  1,  4,  4,  8, 1, 4, 1, 2)    \
  X(17, 32,  1,  2,  8,  4, 1, 4, 1, 2)    \
  X(18, 32,  2,  2,  4,  8, 1, 4, 1, 2)    \
  X(19, 32,  1,  2,  4,  8, 0, 4, 1, 2)    \
  X(20, 32,  1,  2,  4,  8, 1, 2, 1, 2)    \
  X(21, 32,  2,  1,  4,  8, 1, 4, 1, 2)    \
  X(22, 16,  1,  2,  8,  4, 1, 4, 1, 2)    \
  X(23, 16,  1,  2,  8,  4, 1, 2, 1, 2)    \
  X(24, 16,  1,  2,  4,  8, 1, 4, 1, 2)    \
  X(25, 16,  1,  4,  8,  4, 1, 4, 1, 2)    \
  X(26, 16,  1,  2,  8,  2, 1, 2, 1, 2)    \
  X(27, 16,  1,  4,  4,  4, 1, 4, 1, 2)

#define CA_BASE 100
#define NCACFG 28

template<int BM, int NW, int WN, int KS, int KCH, int SB, int PD, int EV, int AR,
         int CA>
static void run(const void* bp, const void* x, const void* sc, const void* zp,
                void* out, int M, int N, int K, cudaStream_t st) {
  constexpr int BN   = 32 * NW * WN;
  constexpr int NTHR = 32 * WN * KS;
  const int RB   = min(BM, M);
  const int smA  = (CA ? 2 : 1) * RB * (K / KCH + 8) * 2 + (CA == 2 ? 16 : 0);
  const int smR  = KS * RB * BN * 4;      // one private slab per k-split group
  const int smem = smA > smR ? smA : smR;
  static int cur = -1;
  if (smem > 48 * 1024 && smem != cur) {
    cudaFuncSetAttribute(
        (const void*)w4a16_kern<BM, NW, WN, KS, KCH, SB, PD, EV, AR, CA>,
        cudaFuncAttributeMaxDynamicSharedMemorySize, smem);
    cur = smem;
  }
  dim3 grid((M + BM - 1) / BM, N / BN);
  w4a16_kern<BM, NW, WN, KS, KCH, SB, PD, EV, AR, CA><<<grid, NTHR, smem, st>>>(
      (const uint32_t*)bp, (const __nv_bfloat16*)x, (const __nv_bfloat16*)sc,
      (const __nv_bfloat16*)zp, (__nv_bfloat16*)out, M, N, K);
}

extern "C" void w4a16_launch(const void* bp, const void* x, const void* sc,
                             const void* zp, void* out,
                             int M, int N, int K, int cfg, int ar,
                             cudaStream_t st) {
  if (cfg >= WG_BASE) {
    switch (cfg - WG_BASE) {
#define W(ID,WG,MN,PD,TM,ZS,NB,SY) \
      case ID: run_wg<WG,MN,PD,TM,ZS,NB,SY>(bp,x,sc,zp,out,M,N,K,st); return;
      WG_LIST
#undef W
    }
    return;
  }
  if (cfg >= GEMV_BASE) {
    switch (cfg - GEMV_BASE) {
#define G(ID,NW,KS,CW,PD,KB) \
      case ID: run_gemv<NW,KS,CW,PD>(bp,x,sc,zp,out,N,K,KB,st); return;
      GEMV_LIST
#undef G
    }
    return;
  }
  if (cfg >= BIGM_BASE) {
    switch (cfg - BIGM_BASE) {
#define Z(ID,BM,NW,WM,WN,PD) \
      case ID: run_bigm<BM,NW,WM,WN,PD>(bp,x,sc,zp,out,M,N,K,st); return;
      BIGM_LIST
#undef Z
    }
    return;
  }
  if (cfg >= CA_BASE) {
    if (ar == 1) {
      switch (cfg - CA_BASE) {
#define X(ID,BM,NW,WN,KS,KCH,SB,PD,EV,CA) \
        case ID: run<BM,NW,WN,KS,KCH,SB,PD,EV,1,CA>(bp,x,sc,zp,out,M,N,K,st); return;
        CACFG_LIST
#undef X
      }
    } else {
      switch (cfg - CA_BASE) {
#define X(ID,BM,NW,WN,KS,KCH,SB,PD,EV,CA) \
        case ID: run<BM,NW,WN,KS,KCH,SB,PD,EV,2,CA>(bp,x,sc,zp,out,M,N,K,st); return;
        CACFG_LIST
#undef X
      }
    }
    return;
  }
  if (ar == 1) {
    switch (cfg) {
#define X(ID,BM,NW,WN,KS,KCH,SB,PD,EV) \
      case ID: run<BM,NW,WN,KS,KCH,SB,PD,EV,1,0>(bp,x,sc,zp,out,M,N,K,st); return;
      CFG_LIST
#undef X
    }
  } else {
    switch (cfg) {
#define X(ID,BM,NW,WN,KS,KCH,SB,PD,EV) \
      case ID: run<BM,NW,WN,KS,KCH,SB,PD,EV,2,0>(bp,x,sc,zp,out,M,N,K,st); return;
      CFG_LIST
#undef X
    }
  }
}

// BM, BN, NTHR, KS, KCH, SB       (big-M: BM, BN, NTHR, MT, NT, PD)
//                                 (gemv:  KB, COLS, NTHR, KS, NW, PD)
//                                 (wg:    MN, BN, NTHR, WG, PD, 0)
extern "C" void w4a16_cfg(int cfg, int* v) {
  if (cfg >= WG_BASE) {
    switch (cfg - WG_BASE) {
#define W(ID,WG,MN,PD,TM,ZS,NB,SY)                                            \
      case ID: v[0]=MN; v[1]=64*WG; v[2]=128*WG; v[3]=WG; v[4]=PD; v[5]=TM;   \
               return;
      WG_LIST
#undef W
    }
    for (int i = 0; i < 6; ++i) v[i] = 0;
    return;
  }
  if (cfg >= GEMV_BASE) {
    switch (cfg - GEMV_BASE) {
#define G(ID,NW,KS,CW,PD,KB)                                                  \
      case ID: v[0]=KB; v[1]=CW*NW*32; v[2]=32*KS*CW; v[3]=KS; v[4]=NW;       \
               v[5]=PD; return;
      GEMV_LIST
#undef G
    }
    for (int i = 0; i < 6; ++i) v[i] = 0;
    return;
  }
  if (cfg >= BIGM_BASE) {
    switch (cfg - BIGM_BASE) {
#define Z(ID,BM,NW,WM,WN,PD)                                                  \
      case ID: v[0]=BM; v[1]=32*NW*WN; v[2]=32*WM*WN; v[3]=BM/(16*WM);        \
               v[4]=4*NW; v[5]=PD; return;
      BIGM_LIST
#undef Z
    }
    for (int i = 0; i < 6; ++i) v[i] = 0;
    return;
  }
  if (cfg >= CA_BASE) {
    switch (cfg - CA_BASE) {              // negative KCH marks a cp.async config
#define X(ID,BM,NW,WN,KS,KCH,SB,PD,EV,CA)                                  \
      case ID: v[0]=BM; v[1]=32*NW*WN; v[2]=32*WN*KS; v[3]=KS; v[4]=-KCH;   \
               v[5]=SB + 10 * CA; return;
      CACFG_LIST
#undef X
    }
    for (int i = 0; i < 6; ++i) v[i] = 0;
    return;
  }
  switch (cfg) {
#define X(ID,BM,NW,WN,KS,KCH,SB,PD,EV)                                      \
    case ID: v[0]=BM; v[1]=32*NW*WN; v[2]=32*WN*KS; v[3]=KS; v[4]=KCH;      \
             v[5]=SB; return;
    CFG_LIST
#undef X
  }
  for (int i = 0; i < 6; ++i) v[i] = 0;
}

extern "C" int w4a16_ncfg() { return CA_BASE + NCACFG; }
'''


# wgmma's accumulator lives in MN/2 registers named one by one in the asm
# operand list, so each MN needs its own literal instruction.  RS form:
# {d}, {a0..a3}, desc_b, scale_d, scaleA, scaleB, tnspB.
def _wgmma_ptx(n: int) -> str:
    na = n // 2
    accs = ",".join(f"%{i}" for i in range(na))
    outs = ",\n        ".join(f'"+f"(d[{i}])' for i in range(na))
    return f'''
DEVI void wgmma{n}(float* d, const uint32_t* a, uint64_t db) {{
  asm volatile("{{\\n"
      ".reg .pred p;\\n"
      "setp.ne.b32 p, %{na + 5}, 0;\\n"
      "wgmma.mma_async.sync.aligned.m64n{n}k16.f32.bf16.bf16 "
      "{{{accs}}}, {{%{na},%{na + 1},%{na + 2},%{na + 3}}}, %{na + 4}, p, 1, 1, 0;\\n"
      "}}\\n"
      : {outs}
      : "r"(a[0]), "r"(a[1]), "r"(a[2]), "r"(a[3]), "l"(db), "n"(1));
}}
template<> DEVI void wgmmaN<{n}>(float* d, const uint32_t* a, uint64_t b) {{
  wgmma{n}(d, a, b);
}}
'''


_WG_MNS = (256,)                                # every MN named in WG_LIST
_CUDA = _CUDA.replace("//@WGMMA@",
                      "".join(_wgmma_ptx(n) for n in _WG_MNS))

_CPP = r'''
#include <torch/extension.h>
#include <c10/cuda/CUDAStream.h>
#include <cstdint>

extern "C" void w4a16_launch(const void*, const void*, const void*, const void*,
                             void*, int, int, int, int, int, cudaStream_t);
extern "C" void w4a16_cfg(int, int*);
extern "C" int w4a16_ncfg();

void w4a16(int64_t bp, int64_t x, int64_t sc, int64_t zp, int64_t out,
           int64_t M, int64_t N, int64_t K, int64_t cfg, int64_t ar) {
  w4a16_launch((const void*)bp, (const void*)x, (const void*)sc, (const void*)zp,
               (void*)out, (int)M, (int)N, (int)K, (int)cfg, (int)ar,
               at::cuda::getCurrentCUDAStream());
}

// A pre-bound launch descriptor.  The harness brackets the whole python call
// with cuda events, so every microsecond spent converting arguments is a
// microsecond the GPU sits idle inside the measured window.  Bind once, then
// re-launch with a single integer argument.
struct Slot {
  const void* bp; const void* x; const void* sc; const void* zp; void* out;
  int M, N, K, cfg, ar;
};
static Slot g_slots[32];

void w4a16_bind(int64_t s, int64_t bp, int64_t x, int64_t sc, int64_t zp,
                int64_t out, int64_t M, int64_t N, int64_t K, int64_t cfg,
                int64_t ar) {
  Slot& q = g_slots[s & 31];
  q.bp = (const void*)bp; q.x = (const void*)x; q.sc = (const void*)sc;
  q.zp = (const void*)zp; q.out = (void*)out;
  q.M = (int)M; q.N = (int)N; q.K = (int)K; q.cfg = (int)cfg; q.ar = (int)ar;
}

void w4a16_go(int64_t s) {
  const Slot& q = g_slots[s & 31];
  w4a16_launch(q.bp, q.x, q.sc, q.zp, q.out, q.M, q.N, q.K, q.cfg, q.ar,
               at::cuda::getCurrentCUDAStream());
}

// Baking that same launch into a graph goes one better.  A kernel launch costs
// the driver several microseconds of CPU work, and the harness records its
// start event *before* the python call, so every one of those microseconds is
// measured as GPU idle time inside the window -- ~5us on every shape here.
// cudaGraphLaunch submits one pre-built command instead.  Capture runs on a
// private stream (capturing the default stream is illegal) while the exec is
// launched on whatever stream torch is using, so ordering is unchanged.
static cudaGraphExec_t g_exec[32] = {nullptr};
static cudaStream_t g_capstream = nullptr;

int64_t w4a16_capture(int64_t s) {
  const int i = (int)(s & 31);
  const Slot& q = g_slots[i];
  if (g_exec[i]) { cudaGraphExecDestroy(g_exec[i]); g_exec[i] = nullptr; }
  if (!g_capstream &&
      cudaStreamCreateWithFlags(&g_capstream, cudaStreamNonBlocking) != cudaSuccess)
    return -1;
  // Relaxed: the launcher may still call cudaFuncSetAttribute, which is not a
  // stream operation and must be allowed to run through the capture.
  if (cudaStreamBeginCapture(g_capstream, cudaStreamCaptureModeRelaxed) != cudaSuccess)
    return -2;
  w4a16_launch(q.bp, q.x, q.sc, q.zp, q.out, q.M, q.N, q.K, q.cfg, q.ar,
               g_capstream);
  cudaGraph_t g = nullptr;
  if (cudaStreamEndCapture(g_capstream, &g) != cudaSuccess || !g) {
    cudaGetLastError();
    return -3;
  }
  const cudaError_t e = cudaGraphInstantiateWithFlags(&g_exec[i], g, 0);
  cudaGraphDestroy(g);
  if (e != cudaSuccess) { g_exec[i] = nullptr; cudaGetLastError(); return -4; }
  return 0;
}

void w4a16_replay(int64_t s) {
  cudaGraphLaunch(g_exec[s & 31], at::cuda::getCurrentCUDAStream());
}

std::vector<int64_t> w4a16_meta(int64_t cfg) {
  int v[6];
  w4a16_cfg((int)cfg, v);
  return {v[0], v[1], v[2], v[3], v[4], v[5]};
}

int64_t w4a16_ncfgs() { return w4a16_ncfg(); }

PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
  m.def("w4a16", &w4a16);
  m.def("w4a16_bind", &w4a16_bind);
  m.def("w4a16_go", &w4a16_go);
  m.def("w4a16_capture", &w4a16_capture);
  m.def("w4a16_replay", &w4a16_replay);
  m.def("w4a16_meta", &w4a16_meta);
  m.def("w4a16_ncfgs", &w4a16_ncfgs);
}
'''

# --------------------------------------------------------------------------- #
# build
# --------------------------------------------------------------------------- #

_EXT = None


def _ext():
    global _EXT
    if _EXT is not None:
        return _EXT
    import shutil

    # ninja + pybind11 headers are needed by torch's JIT extension builder.
    if shutil.which("ninja") is None:
        local_bin = os.path.expanduser("~/.local/bin")
        if os.path.isfile(os.path.join(local_bin, "ninja")):
            os.environ["PATH"] = local_bin + os.pathsep + os.environ.get("PATH", "")
    extra = ["-O3", "--use_fast_math", "-lineinfo"]
    cflags = ["-O3"]
    try:
        import pybind11
        inc = f"-I{pybind11.get_include()}"
        cflags.append(inc)
        extra.append(inc)
    except Exception:
        pass
    os.environ["TORCH_CUDA_ARCH_LIST"] = "9.0a"
    from torch.utils.cpp_extension import load_inline
    _EXT = load_inline(
        name="w4a16_sm90_v2",
        cpp_sources=_CPP,
        cuda_sources=_CUDA,
        extra_cflags=cflags,
        extra_cuda_cflags=extra,
        verbose=False,
    )
    return _EXT


# --------------------------------------------------------------------------- #
# weight repacking
# --------------------------------------------------------------------------- #

def _repack(w_q: torch.Tensor, K: int, N: int) -> torch.Tensor:
    """(K//2, N) uint8  ->  (N//32, K//32, 32, 4) int32 mma-fragment layout.

    Thread `lane` of a warp gets 16 contiguous bytes covering a 32k x 32n tile:
    word q = ks*2 + reg, nibble i  = even-k value of n-tile i,
                         nibble i+4 = odd-k value of n-tile i.
    """
    dev = w_q.device
    wu = torch.empty((K, N), dtype=torch.uint8, device=dev)
    wu[0::2] = w_q & 0xF
    wu[1::2] = w_q >> 4

    lane = torch.arange(32, device=dev)
    c = (lane & 3) * 2                      # even-k offset inside the 8-k half
    r = lane >> 2                           # column offset inside the n8 tile
    k32 = torch.arange(K // 32, device=dev).unsqueeze(1) * 32     # (K/32, 1)
    n32 = torch.arange(N // 32, device=dev).unsqueeze(1) * 32     # (N/32, 1)

    words = []
    for q in range(4):
        ks, reg = q >> 1, q & 1
        kk = k32 + ks * 16 + reg * 8 + c.unsqueeze(0)             # (K/32, 32)
        by = []
        for half in range(2):                                     # 0: even, 1: odd
            kx = (kk + half).unsqueeze(0).expand(N // 32, -1, -1)
            nibbles = []
            for i in range(4):
                nn = (n32 + i * 8 + r.unsqueeze(0)).unsqueeze(1)  # (N/32, 1, 32)
                nibbles.append(wu[kx, nn.expand_as(kx)])
            by.append(nibbles[0] | (nibbles[1] << 4))
            by.append(nibbles[2] | (nibbles[3] << 4))
        # byte order: [ve01, ve23, vo01, vo23]  (little endian -> LSB first)
        words.append(torch.stack([by[0], by[1], by[2], by[3]], dim=-1))
    out = torch.stack(words, dim=-2)        # (N/32, K/32, 32, 4, 4) uint8
    return out.contiguous().view(torch.int32).view(N // 32, K // 32, 32, 4)


# nibble i of a word -> k offset, so a lop3 pair is (2i, 2i+1) and lines up
# with a plain 32-bit load of two consecutive x values.
_GPERM = (0, 2, 4, 6, 1, 3, 5, 7)


def _repack_gemv(w_q: torch.Tensor, K: int, N: int) -> torch.Tensor:
    """(K//2, N) uint8  ->  [col block][32-k block][lane][4] int32, for M==1.

    Lane j of a warp owns column j; its uint32 q holds the 8 weights of k
    offsets 8q..8q+7 (permuted), so one warp-wide uint4 load is 512 contiguous
    bytes covering 32 k for 32 columns.
    """
    dev = w_q.device
    u = torch.empty((K, N), dtype=torch.uint8, device=dev)
    u[0::2] = w_q & 0xF
    u[1::2] = w_q >> 4
    u = u.view(K // 32, 32, N // 32, 32)                    # [kb, kk, cb, j]
    o = torch.zeros(K // 32, N // 32, 32, 4, dtype=torch.int64, device=dev)
    for q in range(4):
        for i in range(8):
            o[:, :, :, q] |= u[:, q * 8 + _GPERM[i]].to(torch.int64) << (4 * i)
    o = o.permute(1, 0, 2, 3).contiguous()                  # [cb, kb, j, q]
    o = torch.where(o >= 2 ** 31, o - 2 ** 32, o).to(torch.int32)
    return o.reshape(-1)


def _repack_wg(w_q: torch.Tensor, K: int, N: int) -> torch.Tensor:
    """(K//2, N) uint8  ->  [n64][k64][thread][4] int32, wgmma ALayout_64x16.

    Value i of thread t (i = 2r + h, register r, half h) of a warpgroup covers
        m = (t/4)%8 + 16*(t/32) + 8*(r%2)      <- weight column
        k = 2*(t%4) + h + 8*(r/2)              <- reduction index
    and dq(w, 4r) pulls nibbles r and r+4 into {lo, hi} of a bf16x2 with lo the
    smaller k, so nibble r carries h=0 and nibble r+4 carries h=1.  One uint4
    load per thread then feeds a whole 64-k chunk.
    """
    dev = w_q.device
    u = torch.empty(K, N, dtype=torch.uint8, device=dev)
    u[0::2] = w_q & 0xF
    u[1::2] = w_q >> 4
    uf = u.view(-1)

    t = torch.arange(128, device=dev)
    c = t % 4
    base = (t // 4) % 8 + 16 * (t // 32)
    kq = torch.arange(K // 64, device=dev)
    j = torch.arange(4, device=dev)
    g = torch.arange(N // 64, device=dev)

    out = torch.zeros(N // 64, K // 64, 4, 128, dtype=torch.int64, device=dev)
    for p in range(8):
        r, h = p % 4, p // 4
        kk = (kq.view(-1, 1, 1) * 64 + j.view(1, -1, 1) * 16
              + (2 * c + h + 8 * (r // 2)).view(1, 1, -1))          # (K/64,4,128)
        nn = g.view(-1, 1) * 64 + (base + 8 * (r % 2)).view(1, -1)  # (N/64,128)
        idx = kk.view(1, K // 64, 4, 128) * N + nn.view(N // 64, 1, 1, 128)
        out |= (uf[idx.reshape(-1)].view(N // 64, K // 64, 4, 128)
                .to(torch.int64) << (4 * p))
        del idx, kk, nn
    del u, uf
    out = out.permute(0, 1, 3, 2).contiguous()                      # [g][kq][t][j]
    out = torch.where(out >= 2 ** 31, out - 2 ** 32, out).to(torch.int32)
    return out.reshape(-1)


# --------------------------------------------------------------------------- #
# config selection
# --------------------------------------------------------------------------- #

_SMEM_MAX = 227 * 1024
_CFG_CACHE: dict = {}
_BIGM_BASE = 1000
_BIGM_NCFG = 26
_GEMV_BASE = 2000
_GEMV_NCFG = 51
_WG_BASE = 3000
_WG_NCFG = 5
_GWS_MAX = 14336 * 8                            # g_ws / g_ctr in the kernel
_GCTR_MAX = 512


def _gemv_candidates(ext, N: int, K: int):
    out = []
    for i in range(_GEMV_NCFG):
        cfg = _GEMV_BASE + i
        kb, cols, nthr, ks, nw, pd = ext.w4a16_meta(cfg)
        if cols == 0 or N % cols or nthr > 1024:
            continue
        if kb > 1 and (N * kb > _GWS_MAX or N // cols > _GCTR_MAX):
            continue
        if K % (kb * 128):                      # each block starts on a group
            continue
        kpb = K // kb                           # k handled by one block
        nkb = kpb >> 5
        kbw = nkb // ks                         # 32-k blocks per k-split warp
        if kbw < 4 or kbw % 4 or nkb % ks:
            continue
        if kpb * 4 + ks * cols * 4 > _SMEM_MAX:
            continue
        out.append(cfg)
    return out


def _bigm_candidates(ext, M: int, N: int, K: int):
    """Big-M tiles worth timing: only where a block's rows are mostly real."""
    out = []
    if M < 32:
        return out
    for i in range(_BIGM_NCFG):
        cfg = _BIGM_BASE + i
        bm, bn, nthr, mt, nt, pd = ext.w4a16_meta(cfg)
        if bm == 0 or N % bn or nthr > 1024 or mt < 1:
            continue
        if bm > 2 * M:
            continue
        # accumulators + A fragments + staging must fit the per-thread register
        # budget, which is 65536/nthr capped at 255.
        regs = min(255, 65536 // nthr)
        if mt * nt * 4 + mt * 4 + 48 > regs:
            continue
        if 2 * bm * 136 * 2 > _SMEM_MAX:
            continue
        out.append(cfg)
    return out


def _wg_candidates(ext, M: int, N: int, K: int):
    """Warpgroup tiles worth timing.

    MN is the wgmma's n -- the padded activation rows -- so a block's tensor
    work is MN*64*K no matter how many of those rows are real.  This path is a
    2.0x win at M=256, a wash at M=32 and a loss at M=16, which reads as "MN
    must track M" and is not: n=32 and n=64 wgmma rows were built, were exact,
    and lost anyway, because 2N threads cannot stream the weights fast enough at
    any MN (see the WG_LIST comment).  So the cap keeps this path to shapes
    where MN=256 is under 2x padding and leaves the rest to the mma kernel.

    mn < M would silently drop rows and _autotune ranks on time alone, so that
    bound is load-bearing.  The upper bound is only a filter; with MN=256 the
    only row left, it admits M in [128, 256].
    """
    out = []
    for i in range(_WG_NCFG):
        cfg = _WG_BASE + i
        mn, bn, nthr, wg, pd, _ = ext.w4a16_meta(cfg)
        if mn == 0 or mn < M or mn > max(64, 2 * M) or N % bn or nthr > 1024:
            continue
        # UF widens to 8 when PD does, so the unroll divisibility follows PD.
        if K % 64 or (K // 64) % (8 if pd > 4 else 4) or K % 16:
            continue
        if (8 * mn) % nthr:                     # whole 16B copies per thread
            continue
        if max(4 * 4 * mn * 32 + 32, mn * (bn + 8) * 2) > _SMEM_MAX:
            continue
        out.append(cfg)
    return out


def _candidates(ext, M: int, N: int, K: int):
    if M == 1:
        gv = _gemv_candidates(ext, N, K)
        if gv:
            return gv
    out = _wg_candidates(ext, M, N, K)
    out += _bigm_candidates(ext, M, N, K)
    for cfg in range(ext.w4a16_ncfgs()):
        bm, bn, nthr, ks, kch, sb = ext.w4a16_meta(cfg)
        if bm == 0 or N % bn or nthr > 1024:
            continue
        ca = kch < 0                            # cp.async: two A buffers
        kch = abs(kch)
        cam = sb // 10 if ca else 0             # 1 = cp.async, 2 = bulk (TMA)
        # A bulk copy moves one whole row of the chunk, so the row has to be a
        # multiple of 16 bytes at both ends.
        if cam == 2 and (K // kch) % 8:
            continue
        gpkc = (K // kch) // 128
        if gpkc < ks or gpkc % ks:
            continue
        if M <= 16 and bm != 16:
            continue
        if 16 < M <= 32 and bm not in (16, 32):
            continue
        if M > 32 and bm < 32:
            continue
        rb = min(bm, M)
        smem = max((2 if ca else 1) * rb * (K // kch + 8) * 2
                   + (16 if cam == 2 else 0), ks * rb * bn * 4)
        if smem > _SMEM_MAX:
            continue
        out.append(cfg)
    return out


def _autotune(ext, bpf, rest, cands, ar):
    """Time each candidate the way the harness does: cold L2, one launch.

    `bpf(cfg)` hands back the packed weights that config's kernel family wants,
    building them on first use -- the warpgroup path needs a different layout
    from the mma path, so the pointer cannot be hoisted out of the loop.
    """
    if len(cands) == 1:
        return cands[0]
    try:
        flush = torch.empty(128 << 20, dtype=torch.uint8, device="cuda")
    except Exception:
        flush = None

    def timeit(cfg, args, reps):
        ts = []
        for _ in range(reps):
            if flush is not None:
                flush.zero_()
            # The sync matters: without it s.record() is queued behind the
            # still-running flush, so the kernel launches into a busy GPU and
            # the measurement excludes the cold-start ramp the harness pays.
            # Ranking without it disagrees with the harness on the small shapes.
            torch.cuda.synchronize()
            s, e = torch.cuda.Event(True), torch.cuda.Event(True)
            s.record()
            ext.w4a16(*args, cfg, ar)
            e.record()
            torch.cuda.synchronize()
            ts.append(s.elapsed_time(e))
        return statistics.median(ts)

    # Two stages.  A 5-trial median separates a 60-candidate field into the few
    # that are close, but not those few from each other -- the top configs sit
    # within 1-2% and a noisy pick there costs more than the tuning does.  The
    # refine pass is ~200 extra launches, which is under 30ms and happens once
    # per shape outside any timed region.
    scored = []
    for cfg in cands:
        try:
            args = (bpf(cfg),) + rest
            for _ in range(2):
                ext.w4a16(*args, cfg, ar)
            torch.cuda.synchronize()
        except Exception:
            continue
        scored.append((timeit(cfg, args, 5), cfg, args))
    if not scored:
        del flush
        return cands[0]
    scored.sort(key=lambda r: r[0])
    best, best_t = scored[0][1], float("inf")
    for _, cfg, args in scored[:8]:
        t = timeit(cfg, args, 21)
        if t < best_t:
            best_t, best = t, cfg
    del flush
    return best


# --------------------------------------------------------------------------- #
# module
# --------------------------------------------------------------------------- #

_SLOTS = [0]


class Model(nn.Module):
    def __init__(self, M: int, N: int, K: int, group_size: int = GROUP_SIZE):
        super().__init__()
        assert K % group_size == 0 and K % 2 == 0
        self.M, self.N, self.K = M, N, K
        self.group_size = group_size
        n_groups = K // group_size
        self.register_buffer("w_q", torch.zeros((K // 2, N), dtype=torch.uint8))
        self.register_buffer("scales", torch.zeros((n_groups, N), dtype=torch.bfloat16))
        self.register_buffer("zeros", torch.zeros((n_groups, N), dtype=torch.bfloat16))
        self._ready = False
        self._slot = _SLOTS[0] & 31
        _SLOTS[0] += 1
        self._x = None            # tensor whose launch descriptor is bound
        self._out = None
        self._go = None
        self._fire = None         # graph replay, or the plain launch

    # -- one-time weight preparation (layout change only, no math) ----------
    def _prepare(self):
        ext = _ext()
        self._sc = self.scales.contiguous()
        self._zp = (self.zeros.float() + 128.0).to(torch.bfloat16).contiguous()
        self._psc = self._sc.data_ptr()
        self._pzp = self._zp.data_ptr()
        self._packs = {}
        self._ext = ext
        self._go = ext.w4a16_go
        self._bind = ext.w4a16_bind
        self._cfg_for = {}
        self._outs = {}
        self._ready = True

    # Each kernel family wants its own B layout -- the M==1 kernel, the mma
    # kernels, and the warpgroup kernel all differ -- so pack on demand and
    # keep whichever ones autotuning actually asked for.
    @staticmethod
    def _kind(M: int, cfg: int) -> str:
        if cfg >= _WG_BASE:
            return "wg"
        return "gemv" if M == 1 else "mma"

    def _bptr(self, kind: str) -> int:
        p = self._packs.get(kind)
        if p is None:
            p = (_repack_gemv(self.w_q, self.K, self.N) if kind == "gemv"
                 else _repack_wg(self.w_q, self.K, self.N) if kind == "wg"
                 else _repack(self.w_q, self.K, self.N))
            self._packs[kind] = p
        return p.data_ptr()

    def _cfg(self, M: int, xp: int, outp: int):
        key = (M, self.N, self.K)
        ar = 1 if M <= 8 else 2
        if key in _CFG_CACHE:
            return _CFG_CACHE[key], ar
        cands = _candidates(self._ext, M, self.N, self.K)
        rest = (xp, self._psc, self._pzp, outp, M, self.N, self.K)
        cfg = _autotune(self._ext, lambda c: self._bptr(self._kind(M, c)),
                        rest, cands, ar)
        _CFG_CACHE[key] = cfg
        return cfg, ar

    def _rebind(self, x: torch.Tensor) -> torch.Tensor:
        """Slow path: (re)build the launch descriptor for this input tensor."""
        if not self._ready:
            self._prepare()
        M = x.shape[0]
        out = self._outs.get(M)
        if out is None or out.device != x.device:
            out = torch.empty((M, self.N), dtype=torch.bfloat16, device=x.device)
            self._outs[M] = out
        ent = self._cfg_for.get(M)
        if ent is None:
            ent = self._cfg(M, x.data_ptr(), out.data_ptr())
            self._cfg_for[M] = ent
        cfg, ar = ent
        bp = self._bptr(self._kind(M, cfg))
        self._bind(self._slot, bp, x.data_ptr(), self._psc, self._pzp,
                   out.data_ptr(), M, self.N, self.K, cfg, ar)
        self._out = out
        self._x = x
        self._go(self._slot)
        # Now that this exact launch has run once (so any lazily-set function
        # attribute is already in place), bake it into a graph for the repeat
        # calls.  Capture is only ever entered from this slow path, so a graph
        # can never outlive the descriptor it was built from.
        self._fire = self._go
        try:
            if not os.environ.get("W4A16_NOGRAPH") and \
                    self._ext.w4a16_capture(self._slot) == 0:
                self._fire = self._ext.w4a16_replay
        except Exception:
            pass
        return out

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        if x is self._x:
            self._fire(self._slot)
            return self._out
        return self._rebind(x)

    # nn.Module.__call__ costs ~1.6us of hook bookkeeping per call, which the
    # harness measures as GPU idle time.  No hooks are used here.
    __call__ = forward


M = 1
N = 12288
K = 4096


def get_inputs():
    return [torch.randn(M, K, dtype=torch.bfloat16)]


def get_init_inputs():
    return [M, N, K]

20260725_085708_or-opus_anthropic_claude-opus-5_07_w4a16_gemm