main
cu 2,106 lines 86 KB
Raw
1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 //
4 // W4A16 GPTQ WMMA prefill kernel for AMD RDNA3 (gfx1100). This is the
5 // matrix-instruction path for M >= 16; small-M decode lives in the sibling
6 // file q_gemm_rdna3.cu and is exposed via a different op
7 // (`gptq_gemm_rdna3`). Keeping the two paths in separate translation units
8 // is intentional: an earlier attempt at putting WMMA in the same TU as the
9 // scalar dot-product kernel introduced a compile-time interaction that
10 // silently miscompiled the M=1 path even though the WMMA template was never
11 // instantiated for M=1. Hipcc's optimizer appears to scope some decisions
12 // at the TU level (likely register file / SGPR pressure heuristics across
13 // all kernels in the TU), so we isolate.
14 //
15 // Hardware notes (RDNA3 / gfx1100 / RX 7900 XTX):
16 // * v_wmma_f32_16x16x16_{f16,bf16}_w32 — 16×16×16 GEMM in one instruction
17 // (~16 cycles per WMMA on wave32). Accumulator dtype is FP32; inputs
18 // are fp16 or bf16. There is NO 16x16x16 with fp16/bf16 accumulator on
19 // gfx11 that we'd want here (we always need fp32 accum to avoid loss
20 // across many K iterations).
21 // * Wave32 input fragment storage is "doubled" — lanes 16..31 hold a
22 // copy of lanes 0..15 for the A and B fragments. The output C
23 // fragment uses a different mapping: lane t holds COLUMN n=lane_lo
24 // of the 16x16 output, with 8 elements alternating M rows by lane_hi
25 // (lanes 0..15 = even rows, lanes 16..31 = odd rows). See the layout
26 // diagram on `gemm_q4_wmma_kernel_16x16_1w` below for the full mapping.
27 // * No native v_global_atomic_pk_add_{f16,bf16} on gfx11; the K-split
28 // epilogue (gridDim.z > 1) emulates packed atomic add via a CAS-32
29 // retry loop on a uint32 word covering 2 fp16/bf16 lanes. Within a
30 // block we shuffle adjacent lanes via shfl_xor first so each pair of
31 // output cols goes through a single atomic — no intra-block
32 // contention. K_SPLIT == 1 keeps the original direct-write path with
33 // each block owning its 16M × 16N output tile.
34
35 #include <cstdint>
36
37 #include <torch/all.h>
38 #include <c10/cuda/CUDAGuard.h>
39 #include <ATen/cuda/CUDAContext.h>
40
41 #include <hip/hip_runtime.h>
42 #include <hip/hip_bf16.h>
43 #include <hip/hip_fp16.h>
44
45 #include "qdq_4_rdna3.cuh"
46
47 #if defined(__HIPCC__) && defined(__gfx1100__)
48 #define __HIP__RDNA3__
49 #endif
50
51 namespace vllm {
52 namespace gptq_rdna3_wmma {
53
54 // Pull dequant types from the sibling namespace.
55 using vllm::gptq_rdna3::bf162_t;
56 using vllm::gptq_rdna3::bf16_t;
57
58 // Device code below uses RDNA3-only __builtin_amdgcn_wmma_* intrinsics;
59 // non-RDNA3 device passes fall through to empty __global__ stubs at the
60 // #else block at the end of this TU.
61 #if defined(__HIP__RDNA3__) || !defined(__HIP_DEVICE_COMPILE__)
62
63 // PRECISE dequant variants live HERE, not in the shared qdq_4_rdna3.cuh
64 // header. Reason: hipcc takes different register/scheduling decisions when
65 // the shared header grows, which caused a measurable decode-tk/s regression
66 // in the scalar kernel even when these new functions were never called from
67 // it. Keeping them in the WMMA TU only restores scalar's binary identity
68 // to its tuned baseline.
69 //
70 // Numerics: the classic fp16 bit-trick (FMA form: q*scale +
71 // (-(1024+zero)*scale)) loses up to ~0.025 per cell at scale=0.1 because
72 // fp16(scale) ≠ scale and the FMA amplifies that by 1024× without cancelling
73 // against the precomputed (1024+zero)*scale (which is rounded to fp16 BEFORE
74 // the FMA).
75 //
76 // Fix: subtract (1024+zero) as an integer FIRST — exact in fp16 because
77 // integers in [1024, 2047] are exactly representable — then multiply by
78 // scale, incurring at most one half-ULP rounding. Costs one extra
79 // instruction per dequant pair (sub+mul vs single FMA), worth it for the
80 // WMMA path because K can be small (16) and errors don't average.
81 __forceinline__ __device__ void prep_zero_scale_fp16_precise(uint32_t zero,
82 half scale,
83 half2& z_prep,
84 half2& y_prep) {
85 union {
86 uint16_t u;
87 half h;
88 } zu;
89 zu.u = (uint16_t)(0x6400 | zero);
90 z_prep = __half2half2(zu.h);
91 y_prep = __half2half2(scale);
92 }
93
94 __forceinline__ __device__ void dequant_4bit_8_fp16_precise(uint32_t qa,
95 half2 (&dq)[4],
96 half2 z_prep,
97 half2 y_prep) {
98 const uint32_t c0 = 0x64006400;
99 union {
100 uint32_t u;
101 half2 h2;
102 } q0, q1, q2, q3;
103 q0.u = ((qa >> 0) & 0x000F000F) | c0;
104 q1.u = ((qa >> 4) & 0x000F000F) | c0;
105 q2.u = ((qa >> 8) & 0x000F000F) | c0;
106 q3.u = ((qa >> 12) & 0x000F000F) | c0;
107 dq[0] = __hmul2(__hsub2(q0.h2, z_prep), y_prep);
108 dq[1] = __hmul2(__hsub2(q1.h2, z_prep), y_prep);
109 dq[2] = __hmul2(__hsub2(q2.h2, z_prep), y_prep);
110 dq[3] = __hmul2(__hsub2(q3.h2, z_prep), y_prep);
111 }
112
113 // fp32 prep for the bf16→bf16 fp32-internal dequant. Returns:
114 // z_prep = -(128 + zero) * scale (folded bias for FMA)
115 // y_prep = scale
116 // Per-element FMA `q*y_prep + z_prep` then yields scale * (nibble - zero).
117 __forceinline__ __device__ void prep_zero_scale_bf16_f32(uint32_t zero,
118 bf16_t scale,
119 float& z_prep,
120 float& y_prep) {
121 float scale_f = __bfloat162float(scale);
122 z_prep = -(128.0f + (float)zero) * scale_f;
123 y_prep = scale_f;
124 }
125
126 // fp32 → bf16 narrow that skips the defensive NaN-canonicalisation hipcc
127 // emits for __float2bfloat16. Round-half-to-even via add (0x7FFF + lsb)
128 // + truncate; no NaN check, since dequant outputs are bounded products
129 // of (nibble - zero) ∈ [-15, 15] and bf16 scales — never NaN/Inf.
130 __forceinline__ __device__ bf16_t f32_to_bf16_no_canon(float f) {
131 uint32_t fu = __float_as_uint(f);
132 uint32_t lsb = (fu >> 16) & 1u;
133 uint16_t out_u = (uint16_t)((fu + 0x7FFFu + lsb) >> 16);
134 bf16_t out;
135 __builtin_memcpy(&out, &out_u, sizeof(out));
136 return out;
137 }
138
139 // 4-bit GPTQ dequant for the bf16 WMMA path: 8 nibbles per int32 in qa,
140 // outputs bf162_t dq[4]. Implementation rationale:
141 //
142 // gfx11 has no v_pk_fma_bf16, so __hmul2/__hsub2 on bf16 lower to a
143 // widen-fp32-op-narrow chain that hipcc decorates with NaN canonicalisation
144 // (28 extra VALU ops per call observed in the v5 ISA dump: v_cmp_u_f32 +
145 // v_cndmask_b32 around every bf16 sub/mul). Doing the math in fp32 directly
146 // — bit-cast widening (`__uint_as_float((bf16_bits) << 16)` is just a shift,
147 // no NaN canon), one fused FMA per element, single `__float2bfloat16` narrow
148 // at the end — eliminates the canon and is also strictly more precise (one
149 // rounding step instead of two).
150 //
151 // Inputs match prep_zero_scale_bf16_f32:
152 // z_prep = -(128 + zero) * scale (folded bias for FMA)
153 // y_prep = scale
154 // Per-element FMA `q*y_prep + z_prep` then yields scale * (nibble - zero).
155 __forceinline__ __device__ void dequant_4bit_8_bf16_to_bf16(uint32_t qa,
156 bf162_t (&dq)[4],
157 float z_prep,
158 float y_prep) {
159 const uint32_t c0 = 0x43004300;
160 const uint32_t q0 = ((qa >> 0) & 0x000F000F) | c0;
161 const uint32_t q1 = ((qa >> 4) & 0x000F000F) | c0;
162 const uint32_t q2 = ((qa >> 8) & 0x000F000F) | c0;
163 const uint32_t q3 = ((qa >> 12) & 0x000F000F) | c0;
164 // bf16(128+nibble) bits → fp32 via left-shift by 16 (zero-extends mantissa).
165 const float q0x = __uint_as_float((q0 & 0xFFFFu) << 16);
166 const float q0y = __uint_as_float(q0 & 0xFFFF0000u);
167 const float q1x = __uint_as_float((q1 & 0xFFFFu) << 16);
168 const float q1y = __uint_as_float(q1 & 0xFFFF0000u);
169 const float q2x = __uint_as_float((q2 & 0xFFFFu) << 16);
170 const float q2y = __uint_as_float(q2 & 0xFFFF0000u);
171 const float q3x = __uint_as_float((q3 & 0xFFFFu) << 16);
172 const float q3y = __uint_as_float(q3 & 0xFFFF0000u);
173 // r = q*scale + (-(128+zero)*scale) = (nibble - zero)*scale, then narrow.
174 dq[0].x = f32_to_bf16_no_canon(__fmaf_rn(q0x, y_prep, z_prep));
175 dq[0].y = f32_to_bf16_no_canon(__fmaf_rn(q0y, y_prep, z_prep));
176 dq[1].x = f32_to_bf16_no_canon(__fmaf_rn(q1x, y_prep, z_prep));
177 dq[1].y = f32_to_bf16_no_canon(__fmaf_rn(q1y, y_prep, z_prep));
178 dq[2].x = f32_to_bf16_no_canon(__fmaf_rn(q2x, y_prep, z_prep));
179 dq[2].y = f32_to_bf16_no_canon(__fmaf_rn(q2y, y_prep, z_prep));
180 dq[3].x = f32_to_bf16_no_canon(__fmaf_rn(q3x, y_prep, z_prep));
181 dq[3].y = f32_to_bf16_no_canon(__fmaf_rn(q3y, y_prep, z_prep));
182 }
183
184 // ---------------------------------------------------------------------------
185 // Packed atomic-add helpers used by the K-split epilogue.
186 //
187 // When the kernel is launched with gridDim.z > 1, multiple K-segments
188 // accumulate into the same 16x16 output tile and need atomic write-back.
189 // gfx11 has no native v_global_atomic_pk_add_{f16,bf16}, so we issue a
190 // CAS-loop on a 32-bit word covering 2 packed fp16/bf16 lanes. Within a
191 // block the kernel pairs adjacent lanes via shfl_xor first, so each pair
192 // of cols (n=lane_lo even, lane_lo+1) goes through a SINGLE atomic — no
193 // intra-block contention on the same uint32 target. Inter-block
194 // contention from gridDim.z (4-way at K_SPLIT=4) is the residual cost.
195 // ---------------------------------------------------------------------------
196
197 __forceinline__ __device__ void atomic_add_pk_f16(half2* addr, half2 val) {
198 uint32_t* addr_u = reinterpret_cast<uint32_t*>(addr);
199 uint32_t old = *addr_u;
200 while (true) {
201 half2 cur;
202 __builtin_memcpy(&cur, &old, sizeof(cur));
203 half2 sum = __hadd2(cur, val);
204 uint32_t sum_u;
205 __builtin_memcpy(&sum_u, &sum, sizeof(sum_u));
206 uint32_t prev = atomicCAS(addr_u, old, sum_u);
207 if (prev == old) break;
208 old = prev;
209 }
210 }
211
212 __forceinline__ __device__ void atomic_add_pk_bf16(bf162_t* addr, bf162_t val) {
213 uint32_t* addr_u = reinterpret_cast<uint32_t*>(addr);
214 uint32_t old = *addr_u;
215 while (true) {
216 bf162_t cur;
217 __builtin_memcpy(&cur, &old, sizeof(cur));
218 bf162_t sum = __hadd2(cur, val);
219 uint32_t sum_u;
220 __builtin_memcpy(&sum_u, &sum, sizeof(sum_u));
221 uint32_t prev = atomicCAS(addr_u, old, sum_u);
222 if (prev == old) break;
223 old = prev;
224 }
225 }
226
227 #endif // helpers guard; K-split heuristics below are pure host/device
228 // arithmetic, called from launch_* on non-RDNA3 device passes too.
229
230 // K-split factor heuristic. Returns the gridDim.z to use for a given K.
231 // Aim: each block does at least ~16 K-tiles (= K=256) so the per-block
232 // constant overhead (LDS init, kernel prologue) is amortised. Upper
233 // bound K_SPLIT=4 to cap inter-block atomic contention to 4-way.
234 //
235 // For typical Qwen-class shapes K ∈ {4096, 5120, 11008}, all return 4.
236 // Smaller K (e.g., embedding lookups) fall back to 1 (no split, no
237 // atomic). K must be divisible by (K_SPLIT × 16) for the split to be
238 // valid; the heuristic checks divisibility before raising the factor.
239 __host__ __device__ static inline int compute_wmma_k_split(int size_k) {
240 if (size_k >= 1024 && size_k % 64 == 0) return 4;
241 if (size_k >= 512 && size_k % 32 == 0) return 2;
242 return 1;
243 }
244
245 // M-and-N-aware K-split heuristic for the v3/v4/v5 launchers.
246 //
247 // The original `compute_wmma_k_split` was K-only and always returns 4 for
248 // Qwen-class K, which over-subscribes wave slots and pays the atomic CAS
249 // epilogue once-per-K-segment per output cell. With v3/v4/v5's larger
250 // tiles (64M × 16/32/64N) and 4 resident waves per block, the no-split
251 // grid is often already well-saturated on gfx1100's 96 CUs / 3072 wave
252 // slots — adding gridDim.z just adds atomic overhead.
253 //
254 // Heuristic: compute the no-split block count gridDim.x × gridDim.y, then
255 // pick the smallest K_SPLIT that brings total waves to at least
256 // ~2× over-subscription (~6000 waves for our 3072 slots, i.e. 1500 blocks
257 // at 4 waves/block). Above that threshold, K_SPLIT=1 — direct write, no
258 // atomic.
259 //
260 // Args:
261 // size_m, size_n, size_k — GEMM dims
262 // m_tile, n_tile — block-level M and N tile (64×16 for v3,
263 // 64×32 for v4, 64×64 for v5)
264 //
265 // Returns: gridDim.z divisor (1, 2, or 4), respecting K-divisibility.
266 __host__ __device__ static inline int compute_wmma_k_split_mn(
267 int size_m, int size_n, int size_k, int m_tile, int n_tile) {
268 const int blocks_xy =
269 ((size_n + n_tile - 1) / n_tile) * ((size_m + m_tile - 1) / m_tile);
270 // Target: enough blocks to keep ~2× oversubscription on 96 CUs / 3072
271 // wave slots at 4 waves/block ⇒ ~1500 blocks no-split.
272 constexpr int kTargetBlocksXY = 1500;
273 if (blocks_xy >= kTargetBlocksXY) return 1;
274 if (blocks_xy * 2 >= kTargetBlocksXY && size_k >= 512 && size_k % 32 == 0)
275 return 2;
276 if (blocks_xy * 4 >= kTargetBlocksXY && size_k >= 1024 && size_k % 64 == 0)
277 return 4;
278 // Fall back to the K-only heuristic when blocks are very few (small
279 // models with small N): K-split is the only way to add parallelism.
280 return compute_wmma_k_split(size_k);
281 }
282
283 #if defined(__HIP__RDNA3__) || !defined(__HIP_DEVICE_COMPILE__)
284
285 // Native AMDGPU vector types expected by the WMMA built-ins.
286 using v16fp16 = _Float16 __attribute__((ext_vector_type(16)));
287 using v16bf16 = __bf16 __attribute__((ext_vector_type(16)));
288 using v8fp32 = float __attribute__((ext_vector_type(8)));
289
290 __device__ __forceinline__ v8fp32 wmma_mma(v16fp16 a, v16fp16 b, v8fp32 c) {
291 return __builtin_amdgcn_wmma_f32_16x16x16_f16_w32(a, b, c);
292 }
293 __device__ __forceinline__ v8fp32 wmma_mma(v16bf16 a, v16bf16 b, v8fp32 c) {
294 return __builtin_amdgcn_wmma_f32_16x16x16_bf16_w32(a, b, c);
295 }
296
297 // Map HIP wrapper types (half, __hip_bfloat16) to native compiler types
298 // (_Float16, __bf16) used by the WMMA built-ins. Bitcast is a register
299 // reinterpret in practice.
300 template <typename T>
301 struct WmmaNative;
302 template <>
303 struct WmmaNative<half> {
304 using elem = _Float16;
305 using v16 = v16fp16;
306 };
307 template <>
308 struct WmmaNative<bf16_t> {
309 using elem = __bf16;
310 using v16 = v16bf16;
311 };
312
313 template <typename FROM, typename TO>
314 __device__ __forceinline__ TO bitcast_elem(FROM x) {
315 static_assert(sizeof(FROM) == sizeof(TO),
316 "bitcast_elem requires equal-sized types");
317 TO r;
318 __builtin_memcpy(&r, &x, sizeof(TO));
319 return r;
320 }
321
322 // Per-T tzero (matches the helper in the scalar TU).
323 template <typename T>
324 __device__ __forceinline__ T tzero();
325 template <>
326 __device__ __forceinline__ half tzero<half>() {
327 return __float2half_rn(0.0f);
328 }
329 template <>
330 __device__ __forceinline__ bf16_t tzero<bf16_t>() {
331 return __float2bfloat16(0.0f);
332 }
333
334 #endif // helpers guard (each __global__ below has its own guard so launch_*
335 // host code remains visible to the parser on non-RDNA3 device passes)
336
337 #if defined(__HIP__RDNA3__) || !defined(__HIP_DEVICE_COMPILE__)
338
339 // ===========================================================================
340 // WMMA kernel: 16M × 16N tile per block, 1 wave, full K traversal.
341 //
342 // Wave32 fragment layout (verified empirically with all-modes diagnostic
343 // against a random A and B and the eight candidate output mappings):
344 //
345 // * A frag (row-major in M, K in slot):
346 // lane t, slot i → A[lane_lo][k = i]
347 // Lane axis encodes M (A's row), slot encodes K.
348 // * B frag (col-major in N, K in slot):
349 // lane t, slot i → B[k = i][lane_lo]
350 // Lane axis encodes N (B's column), slot encodes K. K-axis aligns
351 // with A's K-axis (same slot index).
352 // * C frag (output, lane = N, slot = M with hi-bit interleave):
353 // lane t, slot i → C[m = 2*i + lane_hi][n = lane_lo]
354 // Lane axis encodes N (C's column). Each lane holds 8 elements of
355 // its output column, alternating rows: lanes 0..15 (hi=0) hold even
356 // rows m=0,2,4,...,14; lanes 16..31 (hi=1) hold odd rows
357 // m=1,3,5,...,15.
358 // * Both halves of the wave (lanes 0..15 and 16..31) hold IDENTICAL input
359 // fragments (AMD's "doubled" wave32 input layout). Output is split
360 // between halves via lane_hi.
361 //
362 // History note: an earlier version of this kernel loaded B row-major in K
363 // and assumed the output was C[lane_lo][2*i+lane_hi]. That layout passes
364 // all-A=identity tests because A=I makes the K-axis sum collapse, but
365 // implements C = A @ B^T for non-trivial A — the bug only shows up against
366 // random A. A layout probe iterating all four
367 // {row,col} × {row,col} loadings identified mode 1 (A row, B col) with
368 // output [m=2*i+hi][n=lane_lo] as the unique mapping that yields A @ B.
369 // ===========================================================================
370
371 template <typename T>
372 __global__ void gemm_q4_wmma_kernel_16x16_1w(
373 const T* __restrict__ a, const uint32_t* __restrict__ b_q,
374 const uint32_t* __restrict__ b_qzeros, const T* __restrict__ b_scales,
375 T* __restrict__ c, const int size_m, const int size_n, const int size_k,
376 const int groups, const int zero_offset) {
377 using E = typename WmmaNative<T>::elem;
378 using V16 = typename WmmaNative<T>::v16;
379
380 const int m_tile = blockIdx.y * 16;
381 const int n_tile = blockIdx.x * 16;
382 if (m_tile >= size_m || n_tile >= size_n) return;
383
384 const int lane = threadIdx.x; // 0..31
385 const int lane_lo = lane & 15; // row index within fragment
386 const int lane_hi = lane >> 4; // 0 or 1
387
388 v8fp32 c_acc = {0, 0, 0, 0, 0, 0, 0, 0};
389
390 const int groupsize = size_k / groups;
391
392 // K-split: each block in the gridDim.z dimension processes a contiguous
393 // K-segment [k_start, k_end). With gridDim.z > 1, multiple blocks
394 // accumulate into the same output tile and need atomic write-back at
395 // the end. With gridDim.z == 1 the kernel falls back to the original
396 // behaviour: full K range, single writer per cell, direct write.
397 //
398 // The split multiplies the wave count by gridDim.z and proportionally
399 // raises CU saturation — this is the dominant lever for closing the
400 // throughput gap to the fp16 scalar kernel at M >= 16, which already
401 // uses K-split natively (gridDim.z = K/256). At gridDim.z = 4 with
402 // K=4096, WMMA jumps from 17% to ~67% wave-slot saturation.
403 const int k_per_split = size_k / gridDim.z;
404 const int k_start = blockIdx.z * k_per_split;
405 const int k_end = k_start + k_per_split;
406
407 // LDS tile of dequantized B. 16 K rows × 16 N cols.
408 __shared__ T b_lds[16][16];
409
410 for (int k_tile = k_start; k_tile < k_end; k_tile += 16) {
411 // ---- Dequant 16x16 B tile into LDS ----
412 // 32 lanes split 16 N cols × 2 K-octets per col = 32 dequant tasks.
413 const int my_n = lane_lo;
414 const int my_k_octet = lane_hi; // 0 → K[0..7], 1 → K[8..15]
415 const int actual_n = n_tile + my_n;
416
417 if (actual_n < size_n) {
418 const int qk_row = (k_tile / 8) + my_k_octet;
419 const uint32_t qa = b_q[qk_row * size_n + actual_n];
420
421 const int g = k_tile / groupsize;
422 const int qz_idx = g * (size_n / 8) + actual_n / 8;
423 const int qz_shift = (actual_n & 7) * 4;
424 const uint32_t zero_v =
425 ((b_qzeros[qz_idx] >> qz_shift) & 0xF) + (uint32_t)zero_offset;
426 const T scale_t = b_scales[g * size_n + actual_n];
427
428 const int k_base = my_k_octet * 8;
429
430 if constexpr (std::is_same<T, half>::value) {
431 half2 z_prep, y_prep;
432 prep_zero_scale_fp16_precise(zero_v, scale_t, z_prep, y_prep);
433 half2 dq[4];
434 dequant_4bit_8_fp16_precise(qa, dq, z_prep, y_prep);
435 b_lds[k_base + 0][my_n] = __low2half(dq[0]);
436 b_lds[k_base + 1][my_n] = __high2half(dq[0]);
437 b_lds[k_base + 2][my_n] = __low2half(dq[1]);
438 b_lds[k_base + 3][my_n] = __high2half(dq[1]);
439 b_lds[k_base + 4][my_n] = __low2half(dq[2]);
440 b_lds[k_base + 5][my_n] = __high2half(dq[2]);
441 b_lds[k_base + 6][my_n] = __low2half(dq[3]);
442 b_lds[k_base + 7][my_n] = __high2half(dq[3]);
443 } else {
444 float z_f, y_f;
445 prep_zero_scale_bf16_f32(zero_v, scale_t, z_f, y_f);
446 bf162_t dq[4];
447 dequant_4bit_8_bf16_to_bf16(qa, dq, z_f, y_f);
448 b_lds[k_base + 0][my_n] = dq[0].x;
449 b_lds[k_base + 1][my_n] = dq[0].y;
450 b_lds[k_base + 2][my_n] = dq[1].x;
451 b_lds[k_base + 3][my_n] = dq[1].y;
452 b_lds[k_base + 4][my_n] = dq[2].x;
453 b_lds[k_base + 5][my_n] = dq[2].y;
454 b_lds[k_base + 6][my_n] = dq[3].x;
455 b_lds[k_base + 7][my_n] = dq[3].y;
456 }
457 }
458
459 // No __syncthreads() needed: the launch is `dim3 block(32)` = exactly one
460 // wave32, so there is no inter-wave concurrency. Within a wave the
461 // compiler emits `s_waitcnt lgkmcnt(0)` between dependent ds_write/ds_read
462 // pairs automatically, so cross-lane LDS reads (lane 0 reading what
463 // lane 16 wrote into b_lds[8..15][0]) still observe the writes. Keeping
464 // the explicit `__syncthreads()` would emit a wave-level `s_barrier` that
465 // costs ~10-20 cycles every iteration but provides no semantic guarantee
466 // we don't already have for free in single-wave mode.
467
468 // ---- Build A and B fragments, run WMMA ----
469 V16 a_frag, b_frag;
470 const int m_row = m_tile + lane_lo;
471
472 if (m_row < size_m) {
473 const T* a_row = a + m_row * size_k;
474 // Sequential A reads: replace 16 single-element global_load_b16 with
475 // a bulk 32-byte copy. The AMDGPU backend lowers a memcpy of this
476 // size + alignment to two `global_load_b128` instructions. size_k is
477 // a multiple of 16 (TORCH_CHECK above) and k_tile increments by 16,
478 // so k_tile + 16 is always within bounds — no tail handling needed.
479 // Note: we memcpy into the whole vector (`&a_frag`) rather than
480 // `&a_frag[0]`; ext_vector_type element addresses aren't reliably
481 // valid C pointers across compiler versions.
482 static_assert(sizeof(a_frag) == 32, "V16 must be 32 bytes (16 × 2)");
483 __builtin_memcpy(&a_frag, a_row + k_tile, sizeof(a_frag));
484 } else {
485 #pragma unroll
486 for (int i = 0; i < 16; i++) a_frag[i] = (E)0;
487 }
488
489 // B fragment: lane t holds COLUMN n=lane_lo of the B tile (K-axis in
490 // slot, N-axis in lane). This is the AMD WMMA convention for the right
491 // operand of a matrix multiply — K-axis aligns with A's K-axis (also
492 // in slot), enabling per-lane inner products.
493 #pragma unroll
494 for (int i = 0; i < 16; i++) {
495 b_frag[i] = bitcast_elem<T, E>(b_lds[i][lane_lo]);
496 }
497
498 #ifdef VLLM_WMMA_LAYOUT_DEBUG
499 // Diagnostic: skip WMMA, force c_acc to encode (lane, slot) so the
500 // store pattern reveals the C-output lane→matrix mapping. Compile with
501 // -DVLLM_WMMA_LAYOUT_DEBUG to enable. Output: c[m][n] = lane + slot/16.
502 (void)a_frag;
503 (void)b_frag;
504 #pragma unroll
505 for (int i = 0; i < 8; i++) {
506 c_acc[i] = (float)lane + (float)i / 16.0f;
507 }
508 // Run only one K iteration in debug mode so c_acc isn't overwritten.
509 if (k_tile == 0) {
510 k_tile = size_k; // exit loop on next check
511 }
512 #else
513 c_acc = wmma_mma(a_frag, b_frag, c_acc);
514 #endif
515
516 // No __syncthreads() needed before the next iter overwrites b_lds:
517 // single-wave block, and the next iter's ds_write to b_lds is preceded
518 // by a `s_waitcnt lgkmcnt(0)` from the compiler that ensures the WMMA's
519 // ds_read of b_frag has completed before the new ds_write issues.
520 }
521
522 // ---- Store C ----
523 // Lane t holds column n=lane_lo of the output tile, 8 rows determined by
524 // slot i and lane_hi:
525 // lane_hi == 0 → rows 0, 2, 4, ..., 14 (even rows)
526 // lane_hi == 1 → rows 1, 3, 5, ..., 15 (odd rows)
527 // c_acc[i] corresponds to actual row m = 2*i + lane_hi at column lane_lo.
528 if (gridDim.z > 1) {
529 // K-split path: 4 (or whatever the split factor is) K-segments per
530 // output cell contend → atomic accumulation. Caller has zero-init'd c.
531 //
532 // Pair-shuffle to avoid intra-block CAS contention: lanes lane_lo and
533 // lane_lo+1 share the same uint32 atomic target (4 bytes = 2 fp16),
534 // so without pairing they'd hammer the same word. Instead, swap the
535 // c_acc[i] value with the lane_lo+1 neighbour via shfl_xor and have
536 // ONLY the even lane issue a single packed CAS. Inter-block contention
537 // (gridDim.z-way per cell) remains and is the residual atomic cost.
538 const bool is_even_lane = (lane_lo & 1) == 0;
539 const int out_n_pair = n_tile + lane_lo; // valid only on even lane
540 #pragma unroll
541 for (int i = 0; i < 8; i++) {
542 // Wave-wide shuffle: every lane participates so the side-effect is
543 // visible. Only even lanes use the result. shfl_xor with mask 1
544 // swaps with the lane_lo XOR 1 neighbour (same lane_hi → same row).
545 float other_f = __shfl_xor(c_acc[i], 1);
546 if (!is_even_lane) continue;
547
548 const int out_m = m_tile + 2 * i + lane_hi;
549 if (out_m >= size_m || out_n_pair >= size_n) continue;
550
551 T* dst = c + out_m * size_n + out_n_pair;
552 if constexpr (std::is_same<T, half>::value) {
553 // Pack: .x = mine (col=lane_lo even), .y = neighbour (col=lane_lo+1)
554 half2 packed =
555 __halves2half2(__float2half_rn(c_acc[i]), __float2half_rn(other_f));
556 atomic_add_pk_f16(reinterpret_cast<half2*>(dst), packed);
557 } else {
558 bf162_t packed;
559 packed.x = __float2bfloat16(c_acc[i]);
560 packed.y = __float2bfloat16(other_f);
561 atomic_add_pk_bf16(reinterpret_cast<bf162_t*>(dst), packed);
562 }
563 }
564 } else {
565 // gridDim.z == 1: single writer per cell, direct non-atomic write.
566 // Caller can leave c uninitialised (torch::empty) since every cell is
567 // assigned exactly once.
568 const int out_n = n_tile + lane_lo;
569 if (out_n < size_n) {
570 #pragma unroll
571 for (int i = 0; i < 8; i++) {
572 const int out_m = m_tile + 2 * i + lane_hi;
573 if (out_m < size_m) {
574 T* dst = c + out_m * size_n + out_n;
575 if constexpr (std::is_same<T, half>::value) {
576 *dst = __float2half_rn(c_acc[i]);
577 } else {
578 *dst = __float2bfloat16(c_acc[i]);
579 }
580 }
581 }
582 }
583 }
584 }
585
586 #else // non-RDNA3 device pass: empty kernel for symbol parity.
587 template <typename T>
588 __global__ void gemm_q4_wmma_kernel_16x16_1w(const T*, const uint32_t*,
589 const uint32_t*, const T*, T*,
590 const int, const int, const int,
591 const int, const int) {}
592 #endif
593
594 template <typename T>
595 void launch_gemm_q4_wmma_16x16_1w(const T* a, const uint32_t* b_q_weight,
596 const uint32_t* b_qzeros, const T* b_scales,
597 T* c, int size_m, int size_n, int size_k,
598 int groups, int zero_offset,
599 cudaStream_t stream) {
600 // 1 wave per block (32 lanes), 16x16 C tile per block. gridDim.z splits
601 // K so that more blocks (and therefore more waves) are in flight; with
602 // K_SPLIT > 1 the kernel switches to atomic write-back at the epilogue.
603 const int k_split = compute_wmma_k_split(size_k);
604 dim3 block(32);
605 dim3 grid((size_n + 15) / 16, (size_m + 15) / 16, k_split);
606 gemm_q4_wmma_kernel_16x16_1w<T>
607 <<<grid, block, 0, stream>>>(a, b_q_weight, b_qzeros, b_scales, c, size_m,
608 size_n, size_k, groups, zero_offset);
609 }
610
611 #if defined(__HIP__RDNA3__) || !defined(__HIP_DEVICE_COMPILE__)
612
613 // ===========================================================================
614 // 32x16_2w kernel: 2 waves per block, 32M × 16N tile, double-buffered LDS.
615 //
616 // Targets the bf16-WMMA prefill regime (M >= 128) where the v1 single-wave
617 // kernel saturates only ~24% of WMMA peak because each wave does roughly
618 // one v_wmma every ~30-40 cycles (16-cycle wmma latency + dequant + LDS +
619 // global A load all serial inside the single resident wave).
620 //
621 // Two structural changes vs v1:
622 //
623 // * 2 waves per block (64 threads). Both waves cooperate on a 32M×16N
624 // output tile: wave 0 produces rows [0..15], wave 1 rows [16..31].
625 // The B-tile in LDS is shared (only wave 0 dequants); each wave loads
626 // its own A slice from global. With two resident waves the SIMD
627 // scheduler can keep the WMMA pipeline full by interleaving wmmas
628 // from the two waves while the other does dequant / LDS / load work.
629 //
630 // * Double-buffered LDS B-tile (b_lds[2][16][16]). Wave 0 dequants
631 // the K-tile for iter k+1 while both waves consume the K-tile for
632 // iter k. Pulls dequant out of the WMMA-critical path. Costs ~512 B
633 // extra LDS per block (irrelevant — gfx1100 has 64 KB LDS/CU).
634 //
635 // One __syncthreads() per K-iter remains: it ensures wave 0's dequant of
636 // the next K-tile has committed AND both waves have finished reading the
637 // current K-tile before wave 0 wraps around and overwrites it.
638 //
639 // The v2 launcher (`launch_gemm_q4_wmma_32x16_2w`) is the production entry on
640 // the WMMA path and falls back to v1 internally for size_m < 32 — see the
641 // comment at the top of `launch_gemm_q4_wmma_32x16_2w` for the M=16 regression
642 // rationale that justifies the fallback.
643 // ===========================================================================
644
645 template <typename T>
646 __global__ void gemm_q4_wmma_kernel_32x16_2w(
647 const T* __restrict__ a, const uint32_t* __restrict__ b_q,
648 const uint32_t* __restrict__ b_qzeros, const T* __restrict__ b_scales,
649 T* __restrict__ c, const int size_m, const int size_n, const int size_k,
650 const int groups, const int zero_offset) {
651 using E = typename WmmaNative<T>::elem;
652 using V16 = typename WmmaNative<T>::v16;
653
654 const int m_tile = blockIdx.y * 32; // 32-row stride per block
655 const int n_tile = blockIdx.x * 16;
656 if (m_tile >= size_m || n_tile >= size_n) return;
657
658 const int tid = threadIdx.x; // 0..63
659 const int wave_id = tid >> 5; // 0 or 1
660 const int lane = tid & 31;
661 const int lane_lo = lane & 15;
662 const int lane_hi = lane >> 4;
663
664 v8fp32 c_acc = {0, 0, 0, 0, 0, 0, 0, 0};
665
666 const int groupsize = size_k / groups;
667
668 // K-split: each block handles a contiguous K-segment when gridDim.z > 1.
669 const int k_per_split = size_k / gridDim.z;
670 const int k_start = blockIdx.z * k_per_split;
671 const int k_end = k_start + k_per_split;
672
673 // Double-buffered LDS B-tile. 2 × 16K × 16N × sizeof(T) = 1024 B for
674 // fp16/bf16.
675 __shared__ T b_lds[2][16][16];
676
677 // Dequant a 16K × 16N B-tile into b_lds[buf]. Only wave 0 participates
678 // (32 lanes do 32 dequant tasks: 16 N-cols × 2 K-octets per col).
679 auto dequant_into = [&](int buf, int k_tile) {
680 if (wave_id != 0) return;
681
682 const int my_n = lane_lo;
683 const int my_k_octet = lane_hi;
684 const int actual_n = n_tile + my_n;
685
686 if (actual_n >= size_n) return;
687
688 const int qk_row = (k_tile / 8) + my_k_octet;
689 const uint32_t qa = b_q[qk_row * size_n + actual_n];
690
691 const int g = k_tile / groupsize;
692 const int qz_idx = g * (size_n / 8) + actual_n / 8;
693 const int qz_shift = (actual_n & 7) * 4;
694 const uint32_t zero_v =
695 ((b_qzeros[qz_idx] >> qz_shift) & 0xF) + (uint32_t)zero_offset;
696 const T scale_t = b_scales[g * size_n + actual_n];
697
698 const int k_base = my_k_octet * 8;
699
700 if constexpr (std::is_same<T, half>::value) {
701 half2 z_prep, y_prep;
702 prep_zero_scale_fp16_precise(zero_v, scale_t, z_prep, y_prep);
703 half2 dq[4];
704 dequant_4bit_8_fp16_precise(qa, dq, z_prep, y_prep);
705 b_lds[buf][k_base + 0][my_n] = __low2half(dq[0]);
706 b_lds[buf][k_base + 1][my_n] = __high2half(dq[0]);
707 b_lds[buf][k_base + 2][my_n] = __low2half(dq[1]);
708 b_lds[buf][k_base + 3][my_n] = __high2half(dq[1]);
709 b_lds[buf][k_base + 4][my_n] = __low2half(dq[2]);
710 b_lds[buf][k_base + 5][my_n] = __high2half(dq[2]);
711 b_lds[buf][k_base + 6][my_n] = __low2half(dq[3]);
712 b_lds[buf][k_base + 7][my_n] = __high2half(dq[3]);
713 } else {
714 float z_f, y_f;
715 prep_zero_scale_bf16_f32(zero_v, scale_t, z_f, y_f);
716 bf162_t dq[4];
717 dequant_4bit_8_bf16_to_bf16(qa, dq, z_f, y_f);
718 b_lds[buf][k_base + 0][my_n] = dq[0].x;
719 b_lds[buf][k_base + 1][my_n] = dq[0].y;
720 b_lds[buf][k_base + 2][my_n] = dq[1].x;
721 b_lds[buf][k_base + 3][my_n] = dq[1].y;
722 b_lds[buf][k_base + 4][my_n] = dq[2].x;
723 b_lds[buf][k_base + 5][my_n] = dq[2].y;
724 b_lds[buf][k_base + 6][my_n] = dq[3].x;
725 b_lds[buf][k_base + 7][my_n] = dq[3].y;
726 }
727 };
728
729 // Pre-fill buffer 0 with the first K-tile so iter 0 has data to consume.
730 dequant_into(0, k_start);
731 __syncthreads();
732
733 int cur_buf = 0;
734 for (int k_tile = k_start; k_tile < k_end; k_tile += 16) {
735 const int next_buf = 1 - cur_buf;
736 const int k_next = k_tile + 16;
737
738 // Issue dequant of next K-tile (wave 0 only) — overlaps with current
739 // iter's WMMA work below. The LDS write is non-blocking; the sync at
740 // end of iter ensures wave 1 sees it before iter k+1.
741 if (k_next < k_end) {
742 dequant_into(next_buf, k_next);
743 }
744
745 // Load A: each wave loads its own M slice in parallel.
746 const int m_row = m_tile + wave_id * 16 + lane_lo;
747 V16 a_frag, b_frag;
748 if (m_row < size_m) {
749 const T* a_row = a + m_row * size_k;
750 static_assert(sizeof(a_frag) == 32, "V16 must be 32 bytes");
751 __builtin_memcpy(&a_frag, a_row + k_tile, sizeof(a_frag));
752 } else {
753 #pragma unroll
754 for (int i = 0; i < 16; i++) a_frag[i] = (E)0;
755 }
756
757 // Load B from current buffer (both waves read identical data).
758 #pragma unroll
759 for (int i = 0; i < 16; i++) {
760 b_frag[i] = bitcast_elem<T, E>(b_lds[cur_buf][i][lane_lo]);
761 }
762
763 // Each wave issues its own WMMA against its own a_frag + shared b_frag.
764 c_acc = wmma_mma(a_frag, b_frag, c_acc);
765
766 // Sync ensures: (a) wave 0's dequant into next_buf has committed,
767 // (b) both waves are done reading cur_buf — so next iter's overwrite
768 // (cur_buf becomes the previous next_buf and gets reused two iters
769 // later) is race-free.
770 __syncthreads();
771 cur_buf = next_buf;
772 }
773
774 // ---- Store C ----
775 // Each wave owns rows [m_tile + wave_id*16 .. + 16) of the output tile.
776 const int m_tile_wave = m_tile + wave_id * 16;
777
778 if (gridDim.z > 1) {
779 // K-split atomic path. Pair-shuffle within wave to halve atomic count.
780 // shfl_xor here is wave-local (wave32 semantics) so each wave does its
781 // own pairing — the two waves don't interact during the store.
782 const bool is_even_lane = (lane_lo & 1) == 0;
783 const int out_n_pair = n_tile + lane_lo;
784 #pragma unroll
785 for (int i = 0; i < 8; i++) {
786 float other_f = __shfl_xor(c_acc[i], 1);
787 if (!is_even_lane) continue;
788
789 const int out_m = m_tile_wave + 2 * i + lane_hi;
790 if (out_m >= size_m || out_n_pair >= size_n) continue;
791
792 T* dst = c + out_m * size_n + out_n_pair;
793 if constexpr (std::is_same<T, half>::value) {
794 half2 packed =
795 __halves2half2(__float2half_rn(c_acc[i]), __float2half_rn(other_f));
796 atomic_add_pk_f16(reinterpret_cast<half2*>(dst), packed);
797 } else {
798 bf162_t packed;
799 packed.x = __float2bfloat16(c_acc[i]);
800 packed.y = __float2bfloat16(other_f);
801 atomic_add_pk_bf16(reinterpret_cast<bf162_t*>(dst), packed);
802 }
803 }
804 } else {
805 // Single writer per cell, direct non-atomic write.
806 const int out_n = n_tile + lane_lo;
807 if (out_n < size_n) {
808 #pragma unroll
809 for (int i = 0; i < 8; i++) {
810 const int out_m = m_tile_wave + 2 * i + lane_hi;
811 if (out_m < size_m) {
812 T* dst = c + out_m * size_n + out_n;
813 if constexpr (std::is_same<T, half>::value) {
814 *dst = __float2half_rn(c_acc[i]);
815 } else {
816 *dst = __float2bfloat16(c_acc[i]);
817 }
818 }
819 }
820 }
821 }
822 }
823
824 #else // non-RDNA3 device pass: empty kernel for symbol parity.
825 template <typename T>
826 __global__ void gemm_q4_wmma_kernel_32x16_2w(const T*, const uint32_t*,
827 const uint32_t*, const T*, T*,
828 const int, const int, const int,
829 const int, const int) {}
830 #endif
831
832 template <typename T>
833 void launch_gemm_q4_wmma_32x16_2w(const T* a, const uint32_t* b_q_weight,
834 const uint32_t* b_qzeros, const T* b_scales,
835 T* c, int size_m, int size_n, int size_k,
836 int groups, int zero_offset,
837 cudaStream_t stream) {
838 // Fallback to v1 for size_m < 32. With M-tile=32 the v2 block has 2 waves
839 // working on rows [0..15] and [16..31]; at M < 32 the second wave processes
840 // out-of-range M rows (zero-padded a_frag → wmma produces nothing useful)
841 // and just wastes SIMD cycles. Bench measured a +47 % regression at M=16
842 // vs v1 for this reason. The M < 32 case is rare in serving (decode at
843 // max-num-seqs=32 lands at M≈32 steady-state; the M=16 sliver is edge),
844 // but the fallback costs nothing and is the right shape.
845 if (size_m < 32) {
846 launch_gemm_q4_wmma_16x16_1w<T>(a, b_q_weight, b_qzeros, b_scales, c,
847 size_m, size_n, size_k, groups, zero_offset,
848 stream);
849 return;
850 }
851
852 // 2 waves per block (64 threads), 32M × 16N C tile per block.
853 // K-split heuristic shared with v1. With M-tile=32, the natural
854 // grid blocks are halved on Y vs v1 — but each block does 2× the work,
855 // so total wave count is unchanged at the same M when K_SPLIT is equal.
856 const int k_split = compute_wmma_k_split(size_k);
857 dim3 block(64);
858 dim3 grid((size_n + 15) / 16, (size_m + 31) / 32, k_split);
859 gemm_q4_wmma_kernel_32x16_2w<T>
860 <<<grid, block, 0, stream>>>(a, b_q_weight, b_qzeros, b_scales, c, size_m,
861 size_n, size_k, groups, zero_offset);
862 }
863
864 #if defined(__HIP__RDNA3__) || !defined(__HIP_DEVICE_COMPILE__)
865
866 // ===========================================================================
867 // 64x16_4w kernel: 4 waves per block, 64M × 16N tile, double-buffered LDS.
868 //
869 // Targets the prefill plateau observed at M >= 128 in v2 (~144 K tk/s bf16,
870 // ~28 % of WMMA peak). The bottleneck is wmma issue rate per resident wave:
871 // each wave issues at most 1 wmma per ~30-40 cycles. With only 2 waves per
872 // block, two wmmas overlap; the wmma pipeline (16-cycle latency) is mostly
873 // idle.
874 //
875 // Doubling the resident wave count (2 → 4) targets ~2× wmma throughput by
876 // keeping the pipeline closer to full. 64M tile keeps N-tile at 16 (so the
877 // b_lds layout, dequant pattern, and store mapping carry over from v2) — the
878 // only structural changes are:
879 //
880 // * 4 waves cooperate on the 64M × 16N output tile. Wave w produces rows
881 // [16w .. 16w+15] of the M tile.
882 // * Dequant remains on wave 0 only (32 lanes do 32 dequant slots, identical
883 // to v2). Waves 1-3 idle through the dequant phase but their wmmas can
884 // issue concurrently with wave 0's dequant of the *next* K-tile thanks
885 // to the double buffer — net wave occupancy is dominated by the wmma
886 // phase, not the dequant phase.
887 // * One __syncthreads() per K-iter still required (same race as v2).
888 //
889 // Costs: same LDS as v2 (1024 B for the b_lds double buffer). Block has 128
890 // threads vs 64 in v2; gfx1100 supports up to 1024 threads/block so this is
891 // well within budget. Doubles VGPR pressure slightly because the four waves
892 // each hold their own a_frag + c_acc — but each wave's working set is
893 // independent so per-thread VGPR is unchanged.
894 // ===========================================================================
895
896 template <typename T>
897 __global__ void gemm_q4_wmma_kernel_64x16_4w(
898 const T* __restrict__ a, const uint32_t* __restrict__ b_q,
899 const uint32_t* __restrict__ b_qzeros, const T* __restrict__ b_scales,
900 T* __restrict__ c, const int size_m, const int size_n, const int size_k,
901 const int groups, const int zero_offset) {
902 using E = typename WmmaNative<T>::elem;
903 using V16 = typename WmmaNative<T>::v16;
904
905 const int m_tile =
906 blockIdx.y * 64; // 64-row stride per block (4 waves × 16M)
907 const int n_tile = blockIdx.x * 16;
908 if (m_tile >= size_m || n_tile >= size_n) return;
909
910 const int tid = threadIdx.x; // 0..127
911 const int wave_id = tid >> 5; // 0..3
912 const int lane = tid & 31;
913 const int lane_lo = lane & 15;
914 const int lane_hi = lane >> 4;
915
916 v8fp32 c_acc = {0, 0, 0, 0, 0, 0, 0, 0};
917
918 const int groupsize = size_k / groups;
919
920 // K-split: each block handles a contiguous K-segment when gridDim.z > 1.
921 const int k_per_split = size_k / gridDim.z;
922 const int k_start = blockIdx.z * k_per_split;
923 const int k_end = k_start + k_per_split;
924
925 // Double-buffered LDS B-tile. Same layout as v2.
926 __shared__ T b_lds[2][16][16];
927
928 // Dequant a 16K × 16N B-tile into b_lds[buf]. Only wave 0's 32 lanes
929 // participate (16 N-cols × 2 K-octets = 32 dequant slots). Waves 1-3
930 // skip — their wmma can run concurrently with wave 0's next-iter dequant
931 // through the double buffer.
932 auto dequant_into = [&](int buf, int k_tile) {
933 if (wave_id != 0) return;
934
935 const int my_n = lane_lo;
936 const int my_k_octet = lane_hi;
937 const int actual_n = n_tile + my_n;
938
939 if (actual_n >= size_n) return;
940
941 const int qk_row = (k_tile / 8) + my_k_octet;
942 const uint32_t qa = b_q[qk_row * size_n + actual_n];
943
944 const int g = k_tile / groupsize;
945 const int qz_idx = g * (size_n / 8) + actual_n / 8;
946 const int qz_shift = (actual_n & 7) * 4;
947 const uint32_t zero_v =
948 ((b_qzeros[qz_idx] >> qz_shift) & 0xF) + (uint32_t)zero_offset;
949 const T scale_t = b_scales[g * size_n + actual_n];
950
951 const int k_base = my_k_octet * 8;
952
953 if constexpr (std::is_same<T, half>::value) {
954 half2 z_prep, y_prep;
955 prep_zero_scale_fp16_precise(zero_v, scale_t, z_prep, y_prep);
956 half2 dq[4];
957 dequant_4bit_8_fp16_precise(qa, dq, z_prep, y_prep);
958 b_lds[buf][k_base + 0][my_n] = __low2half(dq[0]);
959 b_lds[buf][k_base + 1][my_n] = __high2half(dq[0]);
960 b_lds[buf][k_base + 2][my_n] = __low2half(dq[1]);
961 b_lds[buf][k_base + 3][my_n] = __high2half(dq[1]);
962 b_lds[buf][k_base + 4][my_n] = __low2half(dq[2]);
963 b_lds[buf][k_base + 5][my_n] = __high2half(dq[2]);
964 b_lds[buf][k_base + 6][my_n] = __low2half(dq[3]);
965 b_lds[buf][k_base + 7][my_n] = __high2half(dq[3]);
966 } else {
967 float z_f, y_f;
968 prep_zero_scale_bf16_f32(zero_v, scale_t, z_f, y_f);
969 bf162_t dq[4];
970 dequant_4bit_8_bf16_to_bf16(qa, dq, z_f, y_f);
971 b_lds[buf][k_base + 0][my_n] = dq[0].x;
972 b_lds[buf][k_base + 1][my_n] = dq[0].y;
973 b_lds[buf][k_base + 2][my_n] = dq[1].x;
974 b_lds[buf][k_base + 3][my_n] = dq[1].y;
975 b_lds[buf][k_base + 4][my_n] = dq[2].x;
976 b_lds[buf][k_base + 5][my_n] = dq[2].y;
977 b_lds[buf][k_base + 6][my_n] = dq[3].x;
978 b_lds[buf][k_base + 7][my_n] = dq[3].y;
979 }
980 };
981
982 // Pre-fill buffer 0 with the first K-tile so iter 0 has data to consume.
983 dequant_into(0, k_start);
984 __syncthreads();
985
986 int cur_buf = 0;
987 for (int k_tile = k_start; k_tile < k_end; k_tile += 16) {
988 const int next_buf = 1 - cur_buf;
989 const int k_next = k_tile + 16;
990
991 // Issue dequant of next K-tile (wave 0 only) — overlaps with the wmma
992 // work below across all 4 waves.
993 if (k_next < k_end) {
994 dequant_into(next_buf, k_next);
995 }
996
997 // Each wave loads its own 16M slice of A (wave w handles M-rows
998 // [m_tile + 16w .. m_tile + 16w + 15]).
999 const int m_row = m_tile + wave_id * 16 + lane_lo;
1000 V16 a_frag, b_frag;
1001 if (m_row < size_m) {
1002 const T* a_row = a + m_row * size_k;
1003 static_assert(sizeof(a_frag) == 32, "V16 must be 32 bytes");
1004 __builtin_memcpy(&a_frag, a_row + k_tile, sizeof(a_frag));
1005 } else {
1006 #pragma unroll
1007 for (int i = 0; i < 16; i++) a_frag[i] = (E)0;
1008 }
1009
1010 // Load B from current buffer (all 4 waves read identical data).
1011 #pragma unroll
1012 for (int i = 0; i < 16; i++) {
1013 b_frag[i] = bitcast_elem<T, E>(b_lds[cur_buf][i][lane_lo]);
1014 }
1015
1016 // Each wave issues its own WMMA against its own a_frag + shared b_frag.
1017 // 4 wmmas in flight per block per K-iter.
1018 c_acc = wmma_mma(a_frag, b_frag, c_acc);
1019
1020 __syncthreads();
1021 cur_buf = next_buf;
1022 }
1023
1024 // ---- Store C ---- Each wave owns 16 M-rows of the output tile.
1025 const int m_tile_wave = m_tile + wave_id * 16;
1026
1027 if (gridDim.z > 1) {
1028 // K-split atomic path. Pair-shuffle within wave to halve atomic count.
1029 const bool is_even_lane = (lane_lo & 1) == 0;
1030 const int out_n_pair = n_tile + lane_lo;
1031 #pragma unroll
1032 for (int i = 0; i < 8; i++) {
1033 float other_f = __shfl_xor(c_acc[i], 1);
1034 if (!is_even_lane) continue;
1035
1036 const int out_m = m_tile_wave + 2 * i + lane_hi;
1037 if (out_m >= size_m || out_n_pair >= size_n) continue;
1038
1039 T* dst = c + out_m * size_n + out_n_pair;
1040 if constexpr (std::is_same<T, half>::value) {
1041 half2 packed =
1042 __halves2half2(__float2half_rn(c_acc[i]), __float2half_rn(other_f));
1043 atomic_add_pk_f16(reinterpret_cast<half2*>(dst), packed);
1044 } else {
1045 bf162_t packed;
1046 packed.x = __float2bfloat16(c_acc[i]);
1047 packed.y = __float2bfloat16(other_f);
1048 atomic_add_pk_bf16(reinterpret_cast<bf162_t*>(dst), packed);
1049 }
1050 }
1051 } else {
1052 // Single writer per cell, direct non-atomic write.
1053 const int out_n = n_tile + lane_lo;
1054 if (out_n < size_n) {
1055 #pragma unroll
1056 for (int i = 0; i < 8; i++) {
1057 const int out_m = m_tile_wave + 2 * i + lane_hi;
1058 if (out_m < size_m) {
1059 T* dst = c + out_m * size_n + out_n;
1060 if constexpr (std::is_same<T, half>::value) {
1061 *dst = __float2half_rn(c_acc[i]);
1062 } else {
1063 *dst = __float2bfloat16(c_acc[i]);
1064 }
1065 }
1066 }
1067 }
1068 }
1069 }
1070
1071 #else // non-RDNA3 device pass: empty kernel for symbol parity.
1072 template <typename T>
1073 __global__ void gemm_q4_wmma_kernel_64x16_4w(const T*, const uint32_t*,
1074 const uint32_t*, const T*, T*,
1075 const int, const int, const int,
1076 const int, const int) {}
1077 #endif
1078
1079 template <typename T>
1080 void launch_gemm_q4_wmma_64x16_4w(const T* a, const uint32_t* b_q_weight,
1081 const uint32_t* b_qzeros, const T* b_scales,
1082 T* c, int size_m, int size_n, int size_k,
1083 int groups, int zero_offset,
1084 cudaStream_t stream) {
1085 // Fall back to v2 for M < 64 (would waste 1+ waves on out-of-range rows).
1086 if (size_m < 64) {
1087 launch_gemm_q4_wmma_32x16_2w<T>(a, b_q_weight, b_qzeros, b_scales, c,
1088 size_m, size_n, size_k, groups, zero_offset,
1089 stream);
1090 return;
1091 }
1092
1093 // 4 waves per block (128 threads), 64M × 16N tile per block.
1094 const int k_split = compute_wmma_k_split_mn(size_m, size_n, size_k, 64, 16);
1095 dim3 block(128);
1096 dim3 grid((size_n + 15) / 16, (size_m + 63) / 64, k_split);
1097 gemm_q4_wmma_kernel_64x16_4w<T>
1098 <<<grid, block, 0, stream>>>(a, b_q_weight, b_qzeros, b_scales, c, size_m,
1099 size_n, size_k, groups, zero_offset);
1100 }
1101
1102 #if defined(__HIP__RDNA3__) || !defined(__HIP_DEVICE_COMPILE__)
1103
1104 // ===========================================================================
1105 // 64x32_4w kernel: 4 waves per block, 64M × 32N tile, double-buffered LDS.
1106 //
1107 // Builds on v3 by doubling the N-tile from 16 → 32. Each wave now issues
1108 // 2 wmmas per K-iter (one for cols 0-15, one for cols 16-31, sharing the
1109 // same a_frag). With 4 waves × 2 wmmas = 8 wmmas in flight per K-iter,
1110 // the wmma pipeline gets ~2× more in-flight work than v3 — targeting the
1111 // remaining wmma issue gap on Qwen-class shapes (gate/up, down) where v3
1112 // plateaus at ~22 TFLOPS effective.
1113 //
1114 // Costs:
1115 // * LDS B-tile doubles (2 × 16K × 32N × sizeof(T) = 2048 B for fp16/bf16).
1116 // * Dequant doubles (32 N-cols × 2 K-octets = 64 slots/K-tile). Distributed
1117 // across waves 0-1: wave 0 dequants n=[0..15], wave 1 dequants n=[16..31].
1118 // Waves 2-3 idle on dequant but do wmma work.
1119 // * Per-wave registers: 2 × v8fp32 accumulator (16 fp32 = 32 VGPRs) plus
1120 // b_frag0 + b_frag1 (32 VGPRs total). Within budget.
1121 //
1122 // Mapping invariant (wave-id → output tile slice):
1123 // * Wave w produces M rows [m_tile + 16w .. m_tile + 16w + 15]
1124 // * c_acc0 holds N cols [n_tile + 0 .. n_tile + 15]
1125 // * c_acc1 holds N cols [n_tile + 16 .. n_tile + 31]
1126 // ===========================================================================
1127
1128 template <typename T>
1129 __global__ void gemm_q4_wmma_kernel_64x32_4w(
1130 const T* __restrict__ a, const uint32_t* __restrict__ b_q,
1131 const uint32_t* __restrict__ b_qzeros, const T* __restrict__ b_scales,
1132 T* __restrict__ c, const int size_m, const int size_n, const int size_k,
1133 const int groups, const int zero_offset) {
1134 using E = typename WmmaNative<T>::elem;
1135 using V16 = typename WmmaNative<T>::v16;
1136
1137 const int m_tile = blockIdx.y * 64;
1138 const int n_tile = blockIdx.x * 32; // 32-col stride per block (was 16 in v3)
1139 if (m_tile >= size_m || n_tile >= size_n) return;
1140
1141 const int tid = threadIdx.x; // 0..127
1142 const int wave_id = tid >> 5; // 0..3
1143 const int lane = tid & 31;
1144 const int lane_lo = lane & 15;
1145 const int lane_hi = lane >> 4;
1146
1147 // Two accumulators per wave: c_acc0 covers cols [n_tile..n_tile+15],
1148 // c_acc1 covers cols [n_tile+16..n_tile+31].
1149 v8fp32 c_acc0 = {0, 0, 0, 0, 0, 0, 0, 0};
1150 v8fp32 c_acc1 = {0, 0, 0, 0, 0, 0, 0, 0};
1151
1152 const int groupsize = size_k / groups;
1153
1154 const int k_per_split = size_k / gridDim.z;
1155 const int k_start = blockIdx.z * k_per_split;
1156 const int k_end = k_start + k_per_split;
1157
1158 // Doubled LDS B-tile: [buf][k][n_in_tile=0..31].
1159 __shared__ T b_lds[2][16][32];
1160
1161 // Dequant: 64 slots per K-tile (32 N-cols × 2 K-octets). Distributed
1162 // across waves 0,1: wave w handles n_in_tile in [16w..16w+15], k_oct in
1163 // {0,1}. Each dequanting wave has 32 lanes for 32 dequant slots — perfect
1164 // mapping, identical layout to v2/v3 dequant per wave. Waves 2,3 stay idle
1165 // during dequant; they catch up via the double buffer overlapping the
1166 // wmma of iter k with dequant of iter k+1.
1167 //
1168 // Tried distributing dequant across all 4 waves (16 slots/wave): regressed
1169 // 3% on gate/up M=2048 (357K → 347K tk/s). The dequant is not on the
1170 // critical path; spreading it just adds LDS bank pressure with no gain.
1171 auto dequant_into = [&](int buf, int k_tile) {
1172 if (wave_id >= 2) return;
1173
1174 const int my_n_local = lane_lo; // 0..15 (within wave's N-half)
1175 const int my_n_in_tile = wave_id * 16 + my_n_local; // 0..31 (in 32N tile)
1176 const int my_k_octet = lane_hi;
1177 const int actual_n = n_tile + my_n_in_tile;
1178
1179 if (actual_n >= size_n) return;
1180
1181 const int qk_row = (k_tile / 8) + my_k_octet;
1182 const uint32_t qa = b_q[qk_row * size_n + actual_n];
1183
1184 const int g = k_tile / groupsize;
1185 const int qz_idx = g * (size_n / 8) + actual_n / 8;
1186 const int qz_shift = (actual_n & 7) * 4;
1187 const uint32_t zero_v =
1188 ((b_qzeros[qz_idx] >> qz_shift) & 0xF) + (uint32_t)zero_offset;
1189 const T scale_t = b_scales[g * size_n + actual_n];
1190
1191 const int k_base = my_k_octet * 8;
1192
1193 if constexpr (std::is_same<T, half>::value) {
1194 half2 z_prep, y_prep;
1195 prep_zero_scale_fp16_precise(zero_v, scale_t, z_prep, y_prep);
1196 half2 dq[4];
1197 dequant_4bit_8_fp16_precise(qa, dq, z_prep, y_prep);
1198 b_lds[buf][k_base + 0][my_n_in_tile] = __low2half(dq[0]);
1199 b_lds[buf][k_base + 1][my_n_in_tile] = __high2half(dq[0]);
1200 b_lds[buf][k_base + 2][my_n_in_tile] = __low2half(dq[1]);
1201 b_lds[buf][k_base + 3][my_n_in_tile] = __high2half(dq[1]);
1202 b_lds[buf][k_base + 4][my_n_in_tile] = __low2half(dq[2]);
1203 b_lds[buf][k_base + 5][my_n_in_tile] = __high2half(dq[2]);
1204 b_lds[buf][k_base + 6][my_n_in_tile] = __low2half(dq[3]);
1205 b_lds[buf][k_base + 7][my_n_in_tile] = __high2half(dq[3]);
1206 } else {
1207 float z_f, y_f;
1208 prep_zero_scale_bf16_f32(zero_v, scale_t, z_f, y_f);
1209 bf162_t dq[4];
1210 dequant_4bit_8_bf16_to_bf16(qa, dq, z_f, y_f);
1211 b_lds[buf][k_base + 0][my_n_in_tile] = dq[0].x;
1212 b_lds[buf][k_base + 1][my_n_in_tile] = dq[0].y;
1213 b_lds[buf][k_base + 2][my_n_in_tile] = dq[1].x;
1214 b_lds[buf][k_base + 3][my_n_in_tile] = dq[1].y;
1215 b_lds[buf][k_base + 4][my_n_in_tile] = dq[2].x;
1216 b_lds[buf][k_base + 5][my_n_in_tile] = dq[2].y;
1217 b_lds[buf][k_base + 6][my_n_in_tile] = dq[3].x;
1218 b_lds[buf][k_base + 7][my_n_in_tile] = dq[3].y;
1219 }
1220 };
1221
1222 dequant_into(0, k_start);
1223 __syncthreads();
1224
1225 int cur_buf = 0;
1226 for (int k_tile = k_start; k_tile < k_end; k_tile += 16) {
1227 const int next_buf = 1 - cur_buf;
1228 const int k_next = k_tile + 16;
1229
1230 if (k_next < k_end) {
1231 dequant_into(next_buf, k_next);
1232 }
1233
1234 // Load A: each wave loads its 16M slice, shared across both wmmas.
1235 const int m_row = m_tile + wave_id * 16 + lane_lo;
1236 V16 a_frag, b_frag0, b_frag1;
1237 if (m_row < size_m) {
1238 const T* a_row = a + m_row * size_k;
1239 static_assert(sizeof(a_frag) == 32, "V16 must be 32 bytes");
1240 __builtin_memcpy(&a_frag, a_row + k_tile, sizeof(a_frag));
1241 } else {
1242 #pragma unroll
1243 for (int i = 0; i < 16; i++) a_frag[i] = (E)0;
1244 }
1245
1246 // Load B for cols [0..15] and cols [16..31]. Both halves of the 32N tile.
1247 #pragma unroll
1248 for (int i = 0; i < 16; i++) {
1249 b_frag0[i] = bitcast_elem<T, E>(b_lds[cur_buf][i][lane_lo]);
1250 b_frag1[i] = bitcast_elem<T, E>(b_lds[cur_buf][i][lane_lo + 16]);
1251 }
1252
1253 // Two wmmas per wave per K-iter, sharing a_frag.
1254 c_acc0 = wmma_mma(a_frag, b_frag0, c_acc0);
1255 c_acc1 = wmma_mma(a_frag, b_frag1, c_acc1);
1256
1257 __syncthreads();
1258 cur_buf = next_buf;
1259 }
1260
1261 // ---- Store C ----
1262 // Each wave owns 16M rows × 32N cols. c_acc0 → cols [n_tile..n_tile+15],
1263 // c_acc1 → cols [n_tile+16..n_tile+31].
1264 const int m_tile_wave = m_tile + wave_id * 16;
1265
1266 // Helper: store one v8fp32 accumulator's 8 outputs (covers 16 N-cols at
1267 // n_base via lane_lo + interleaved M rows m_tile_wave + 2i + lane_hi).
1268 auto store_acc = [&](const v8fp32& acc, int n_base) {
1269 if (gridDim.z > 1) {
1270 const bool is_even_lane = (lane_lo & 1) == 0;
1271 const int out_n_pair = n_base + lane_lo;
1272 #pragma unroll
1273 for (int i = 0; i < 8; i++) {
1274 float other_f = __shfl_xor(acc[i], 1);
1275 if (!is_even_lane) continue;
1276
1277 const int out_m = m_tile_wave + 2 * i + lane_hi;
1278 if (out_m >= size_m || out_n_pair >= size_n) continue;
1279
1280 T* dst = c + out_m * size_n + out_n_pair;
1281 if constexpr (std::is_same<T, half>::value) {
1282 half2 packed =
1283 __halves2half2(__float2half_rn(acc[i]), __float2half_rn(other_f));
1284 atomic_add_pk_f16(reinterpret_cast<half2*>(dst), packed);
1285 } else {
1286 bf162_t packed;
1287 packed.x = __float2bfloat16(acc[i]);
1288 packed.y = __float2bfloat16(other_f);
1289 atomic_add_pk_bf16(reinterpret_cast<bf162_t*>(dst), packed);
1290 }
1291 }
1292 } else {
1293 const int out_n = n_base + lane_lo;
1294 if (out_n >= size_n) return;
1295 #pragma unroll
1296 for (int i = 0; i < 8; i++) {
1297 const int out_m = m_tile_wave + 2 * i + lane_hi;
1298 if (out_m < size_m) {
1299 T* dst = c + out_m * size_n + out_n;
1300 if constexpr (std::is_same<T, half>::value) {
1301 *dst = __float2half_rn(acc[i]);
1302 } else {
1303 *dst = __float2bfloat16(acc[i]);
1304 }
1305 }
1306 }
1307 }
1308 };
1309
1310 store_acc(c_acc0, n_tile);
1311 store_acc(c_acc1, n_tile + 16);
1312 }
1313
1314 #else // non-RDNA3 device pass: empty kernel for symbol parity.
1315 template <typename T>
1316 __global__ void gemm_q4_wmma_kernel_64x32_4w(const T*, const uint32_t*,
1317 const uint32_t*, const T*, T*,
1318 const int, const int, const int,
1319 const int, const int) {}
1320 #endif
1321
1322 template <typename T>
1323 void launch_gemm_q4_wmma_64x32_4w(const T* a, const uint32_t* b_q_weight,
1324 const uint32_t* b_qzeros, const T* b_scales,
1325 T* c, int size_m, int size_n, int size_k,
1326 int groups, int zero_offset,
1327 cudaStream_t stream) {
1328 // Fall back to v3 when M < 64 (small-M decode/prefill stays on the
1329 // narrower 64M × 16N path) or when N < 32 (tile would waste a wave on
1330 // out-of-range cols).
1331 if (size_m < 64 || size_n < 32) {
1332 launch_gemm_q4_wmma_64x16_4w<T>(a, b_q_weight, b_qzeros, b_scales, c,
1333 size_m, size_n, size_k, groups, zero_offset,
1334 stream);
1335 return;
1336 }
1337
1338 // 4 waves per block (128 threads), 64M × 32N tile per block.
1339 const int k_split = compute_wmma_k_split_mn(size_m, size_n, size_k, 64, 32);
1340 dim3 block(128);
1341 dim3 grid((size_n + 31) / 32, (size_m + 63) / 64, k_split);
1342 gemm_q4_wmma_kernel_64x32_4w<T>
1343 <<<grid, block, 0, stream>>>(a, b_q_weight, b_qzeros, b_scales, c, size_m,
1344 size_n, size_k, groups, zero_offset);
1345 }
1346
1347 #if defined(__HIP__RDNA3__) || !defined(__HIP_DEVICE_COMPILE__)
1348
1349 // ===========================================================================
1350 // 64x64_4w kernel: 4 waves per block, 64M × 64N tile, 4 wmmas per wave per
1351 // K-iter.
1352 //
1353 // Doubles the N-tile from 32 → 64. Each wave issues 4 wmmas per K-iter
1354 // (cols 0-15, 16-31, 32-47, 48-63), all sharing the same a_frag. With
1355 // 4 waves × 4 wmmas = 16 wmmas in flight per K-iter, the wmma pipeline
1356 // is fully saturated (16-cycle latency × 1 issue/cycle = 16 wmmas in
1357 // flight at peak).
1358 //
1359 // Costs:
1360 // * LDS B-tile: 2 × 16K × 64N × sizeof(T) = 4 KB. Within budget.
1361 // * Dequant: 64 N × 2 K-oct = 128 slots/K-tile. Distributed across all
1362 // 4 waves: 32 slots/wave (16 N-cols × 2 K-octets per wave) — perfect
1363 // 32-lane fit, full lane utilization on dequant.
1364 // * Per-wave registers: 4 × v8fp32 acc (64 VGPRs) + 4 b_frag (64 VGPRs)
1365 // + a_frag (16 VGPRs) ≈ 144 VGPRs/thread + locals ≈ ~170 total.
1366 // Under the 192-VGPR gfx1100 cap.
1367 //
1368 // Mapping invariant:
1369 // * Wave w produces M rows [m_tile + 16w .. m_tile + 16w + 15]
1370 // * c_acc[i] holds N cols [n_tile + 16i .. n_tile + 16i + 15] for i=0..3
1371 // ===========================================================================
1372
1373 template <typename T>
1374 __global__ void gemm_q4_wmma_kernel_64x64_4w(
1375 const T* __restrict__ a, const uint32_t* __restrict__ b_q,
1376 const uint32_t* __restrict__ b_qzeros, const T* __restrict__ b_scales,
1377 T* __restrict__ c, const int size_m, const int size_n, const int size_k,
1378 const int groups, const int zero_offset) {
1379 using E = typename WmmaNative<T>::elem;
1380 using V16 = typename WmmaNative<T>::v16;
1381
1382 const int m_tile = blockIdx.y * 64;
1383 const int n_tile = blockIdx.x * 64; // 64-col stride per block
1384 if (m_tile >= size_m || n_tile >= size_n) return;
1385
1386 const int tid = threadIdx.x; // 0..127
1387 const int wave_id = tid >> 5; // 0..3
1388 const int lane = tid & 31;
1389 const int lane_lo = lane & 15;
1390 const int lane_hi = lane >> 4;
1391
1392 // Four accumulators per wave, each covering 16 N-cols.
1393 v8fp32 c_acc0 = {0, 0, 0, 0, 0, 0, 0, 0};
1394 v8fp32 c_acc1 = {0, 0, 0, 0, 0, 0, 0, 0};
1395 v8fp32 c_acc2 = {0, 0, 0, 0, 0, 0, 0, 0};
1396 v8fp32 c_acc3 = {0, 0, 0, 0, 0, 0, 0, 0};
1397
1398 const int groupsize = size_k / groups;
1399
1400 const int k_per_split = size_k / gridDim.z;
1401 const int k_start = blockIdx.z * k_per_split;
1402 const int k_end = k_start + k_per_split;
1403
1404 // Larger LDS B-tile: [buf][k][n_in_tile=0..63].
1405 __shared__ T b_lds[2][16][64];
1406
1407 // Dequant: 128 slots per K-tile (64 N-cols × 2 K-octets). All 4 waves
1408 // participate; wave w covers n_in_tile in [16w..16w+15], k_oct in {0,1}
1409 // — 32 slots per wave (16 N-cols × 2 K-octets), perfect 32-lane fit.
1410 auto dequant_into = [&](int buf, int k_tile) {
1411 const int my_n_local = lane_lo; // 0..15 within wave
1412 const int my_n_in_tile = wave_id * 16 + my_n_local; // 0..63
1413 const int my_k_octet = lane_hi;
1414 const int actual_n = n_tile + my_n_in_tile;
1415
1416 if (actual_n >= size_n) return;
1417
1418 const int qk_row = (k_tile / 8) + my_k_octet;
1419 const uint32_t qa = b_q[qk_row * size_n + actual_n];
1420
1421 const int g = k_tile / groupsize;
1422 const int qz_idx = g * (size_n / 8) + actual_n / 8;
1423 const int qz_shift = (actual_n & 7) * 4;
1424 const uint32_t zero_v =
1425 ((b_qzeros[qz_idx] >> qz_shift) & 0xF) + (uint32_t)zero_offset;
1426 const T scale_t = b_scales[g * size_n + actual_n];
1427
1428 const int k_base = my_k_octet * 8;
1429
1430 if constexpr (std::is_same<T, half>::value) {
1431 half2 z_prep, y_prep;
1432 prep_zero_scale_fp16_precise(zero_v, scale_t, z_prep, y_prep);
1433 half2 dq[4];
1434 dequant_4bit_8_fp16_precise(qa, dq, z_prep, y_prep);
1435 b_lds[buf][k_base + 0][my_n_in_tile] = __low2half(dq[0]);
1436 b_lds[buf][k_base + 1][my_n_in_tile] = __high2half(dq[0]);
1437 b_lds[buf][k_base + 2][my_n_in_tile] = __low2half(dq[1]);
1438 b_lds[buf][k_base + 3][my_n_in_tile] = __high2half(dq[1]);
1439 b_lds[buf][k_base + 4][my_n_in_tile] = __low2half(dq[2]);
1440 b_lds[buf][k_base + 5][my_n_in_tile] = __high2half(dq[2]);
1441 b_lds[buf][k_base + 6][my_n_in_tile] = __low2half(dq[3]);
1442 b_lds[buf][k_base + 7][my_n_in_tile] = __high2half(dq[3]);
1443 } else {
1444 float z_f, y_f;
1445 prep_zero_scale_bf16_f32(zero_v, scale_t, z_f, y_f);
1446 bf162_t dq[4];
1447 dequant_4bit_8_bf16_to_bf16(qa, dq, z_f, y_f);
1448 b_lds[buf][k_base + 0][my_n_in_tile] = dq[0].x;
1449 b_lds[buf][k_base + 1][my_n_in_tile] = dq[0].y;
1450 b_lds[buf][k_base + 2][my_n_in_tile] = dq[1].x;
1451 b_lds[buf][k_base + 3][my_n_in_tile] = dq[1].y;
1452 b_lds[buf][k_base + 4][my_n_in_tile] = dq[2].x;
1453 b_lds[buf][k_base + 5][my_n_in_tile] = dq[2].y;
1454 b_lds[buf][k_base + 6][my_n_in_tile] = dq[3].x;
1455 b_lds[buf][k_base + 7][my_n_in_tile] = dq[3].y;
1456 }
1457 };
1458
1459 dequant_into(0, k_start);
1460 __syncthreads();
1461
1462 int cur_buf = 0;
1463 for (int k_tile = k_start; k_tile < k_end; k_tile += 16) {
1464 const int next_buf = 1 - cur_buf;
1465 const int k_next = k_tile + 16;
1466
1467 if (k_next < k_end) {
1468 dequant_into(next_buf, k_next);
1469 }
1470
1471 // Load A: each wave loads its 16M slice, shared across all 4 wmmas.
1472 const int m_row = m_tile + wave_id * 16 + lane_lo;
1473 V16 a_frag, b_frag0, b_frag1, b_frag2, b_frag3;
1474 if (m_row < size_m) {
1475 const T* a_row = a + m_row * size_k;
1476 static_assert(sizeof(a_frag) == 32, "V16 must be 32 bytes");
1477 __builtin_memcpy(&a_frag, a_row + k_tile, sizeof(a_frag));
1478 } else {
1479 #pragma unroll
1480 for (int i = 0; i < 16; i++) a_frag[i] = (E)0;
1481 }
1482
1483 // Load B for all four 16-col halves.
1484 #pragma unroll
1485 for (int i = 0; i < 16; i++) {
1486 b_frag0[i] = bitcast_elem<T, E>(b_lds[cur_buf][i][lane_lo + 0]);
1487 b_frag1[i] = bitcast_elem<T, E>(b_lds[cur_buf][i][lane_lo + 16]);
1488 b_frag2[i] = bitcast_elem<T, E>(b_lds[cur_buf][i][lane_lo + 32]);
1489 b_frag3[i] = bitcast_elem<T, E>(b_lds[cur_buf][i][lane_lo + 48]);
1490 }
1491
1492 // Four wmmas per wave per K-iter, sharing a_frag.
1493 c_acc0 = wmma_mma(a_frag, b_frag0, c_acc0);
1494 c_acc1 = wmma_mma(a_frag, b_frag1, c_acc1);
1495 c_acc2 = wmma_mma(a_frag, b_frag2, c_acc2);
1496 c_acc3 = wmma_mma(a_frag, b_frag3, c_acc3);
1497
1498 __syncthreads();
1499 cur_buf = next_buf;
1500 }
1501
1502 // ---- Store C ---- Each wave owns 16M × 64N. Helper writes one acc slice.
1503 const int m_tile_wave = m_tile + wave_id * 16;
1504 auto store_acc = [&](const v8fp32& acc, int n_base) {
1505 if (gridDim.z > 1) {
1506 const bool is_even_lane = (lane_lo & 1) == 0;
1507 const int out_n_pair = n_base + lane_lo;
1508 #pragma unroll
1509 for (int i = 0; i < 8; i++) {
1510 float other_f = __shfl_xor(acc[i], 1);
1511 if (!is_even_lane) continue;
1512
1513 const int out_m = m_tile_wave + 2 * i + lane_hi;
1514 if (out_m >= size_m || out_n_pair >= size_n) continue;
1515
1516 T* dst = c + out_m * size_n + out_n_pair;
1517 if constexpr (std::is_same<T, half>::value) {
1518 half2 packed =
1519 __halves2half2(__float2half_rn(acc[i]), __float2half_rn(other_f));
1520 atomic_add_pk_f16(reinterpret_cast<half2*>(dst), packed);
1521 } else {
1522 bf162_t packed;
1523 packed.x = __float2bfloat16(acc[i]);
1524 packed.y = __float2bfloat16(other_f);
1525 atomic_add_pk_bf16(reinterpret_cast<bf162_t*>(dst), packed);
1526 }
1527 }
1528 } else {
1529 const int out_n = n_base + lane_lo;
1530 if (out_n >= size_n) return;
1531 #pragma unroll
1532 for (int i = 0; i < 8; i++) {
1533 const int out_m = m_tile_wave + 2 * i + lane_hi;
1534 if (out_m < size_m) {
1535 T* dst = c + out_m * size_n + out_n;
1536 if constexpr (std::is_same<T, half>::value) {
1537 *dst = __float2half_rn(acc[i]);
1538 } else {
1539 *dst = __float2bfloat16(acc[i]);
1540 }
1541 }
1542 }
1543 }
1544 };
1545
1546 store_acc(c_acc0, n_tile + 0);
1547 store_acc(c_acc1, n_tile + 16);
1548 store_acc(c_acc2, n_tile + 32);
1549 store_acc(c_acc3, n_tile + 48);
1550 }
1551
1552 // ===========================================================================
1553 // 128x64_k16 kernel: 8 waves per block, 128M × 64N tile, K=16 per iteration.
1554 //
1555 // Doubles M-tile from 64 → 128. Each B-tile in LDS is reused by 8 waves
1556 // (8 independent A-row slices) instead of 4, halving the effective B-load
1557 // cost per output element. This matches Hybrid Triton's BLOCK_M=128.
1558 //
1559 // Dequant: same 128 slots as V5 (64N × 2 K-octets). Only waves 0-3
1560 // participate in dequant (same mapping). Waves 4-7 are pure compute.
1561 // LDS: [2][16][64] × sizeof(T) — unchanged from V5.
1562 // ===========================================================================
1563
1564 template <typename T>
1565 __global__ void gemm_q4_wmma_kernel_128x64_k16(
1566 const T* __restrict__ a, const uint32_t* __restrict__ b_q,
1567 const uint32_t* __restrict__ b_qzeros, const T* __restrict__ b_scales,
1568 T* __restrict__ c, const int size_m, const int size_n, const int size_k,
1569 const int groups, const int zero_offset) {
1570 using E = typename WmmaNative<T>::elem;
1571 using V16 = typename WmmaNative<T>::v16;
1572
1573 const int m_tile = blockIdx.y * 128; // 128-row M tile
1574 const int n_tile = blockIdx.x * 64;
1575 if (m_tile >= size_m || n_tile >= size_n) return;
1576
1577 const int tid = threadIdx.x; // 0..255
1578 const int wave_id = tid >> 5; // 0..7
1579 const int lane = tid & 31;
1580 const int lane_lo = lane & 15;
1581 const int lane_hi = lane >> 4;
1582
1583 v8fp32 c_acc0 = {0, 0, 0, 0, 0, 0, 0, 0};
1584 v8fp32 c_acc1 = {0, 0, 0, 0, 0, 0, 0, 0};
1585 v8fp32 c_acc2 = {0, 0, 0, 0, 0, 0, 0, 0};
1586 v8fp32 c_acc3 = {0, 0, 0, 0, 0, 0, 0, 0};
1587
1588 const int groupsize = size_k / groups;
1589
1590 const int k_per_split = size_k / gridDim.z;
1591 const int k_start = blockIdx.z * k_per_split;
1592 const int k_end = k_start + k_per_split;
1593
1594 // LDS with K-contiguous layout [buf][N][K] — enables vectorized ds_load_b128.
1595 // Triton uses this layout to read 8 bf16 per instruction instead of 1.
1596 __shared__ T b_lds[2][64][16];
1597
1598 // Dequant with scale/zero caching: 1 global_load per iter (7/8 of the time).
1599 const bool dq_ok =
1600 (wave_id < 4) && (n_tile + wave_id * 16 + lane_lo < size_n);
1601 const int dq_n = wave_id * 16 + lane_lo;
1602 const int dq_an = n_tile + dq_n;
1603 const int dq_oct = lane_hi;
1604 const int dq_kb = dq_oct * 8;
1605 half2 ch_z = {}, ch_y = {};
1606 float cf_z = 0, cf_y = 0;
1607 int cached_g = -1;
1608
1609 auto dequant_into = [&](int buf, int k_tile) __attribute__((always_inline)) {
1610 if (!dq_ok) return;
1611
1612 // Reload scale/zero only on group boundary (every groupsize/16 iters).
1613 const int g = k_tile / groupsize;
1614 if (g != cached_g) {
1615 cached_g = g;
1616 const int qz_idx = g * (size_n / 8) + dq_an / 8;
1617 const uint32_t zero_v = ((b_qzeros[qz_idx] >> ((dq_an & 7) * 4)) & 0xF) +
1618 (uint32_t)zero_offset;
1619 const T sc = b_scales[g * size_n + dq_an];
1620 if constexpr (std::is_same<T, half>::value)
1621 prep_zero_scale_fp16_precise(zero_v, sc, ch_z, ch_y);
1622 else
1623 prep_zero_scale_bf16_f32(zero_v, sc, cf_z, cf_y);
1624 }
1625
1626 const int qk_row = (k_tile / 8) + dq_oct;
1627 const uint32_t qa = b_q[qk_row * size_n + dq_an];
1628 const int k_base = dq_kb;
1629
1630 if constexpr (std::is_same<T, half>::value) {
1631 half2 dq[4];
1632 dequant_4bit_8_fp16_precise(qa, dq, ch_z, ch_y);
1633 b_lds[buf][dq_n][k_base + 0] = __low2half(dq[0]);
1634 b_lds[buf][dq_n][k_base + 1] = __high2half(dq[0]);
1635 b_lds[buf][dq_n][k_base + 2] = __low2half(dq[1]);
1636 b_lds[buf][dq_n][k_base + 3] = __high2half(dq[1]);
1637 b_lds[buf][dq_n][k_base + 4] = __low2half(dq[2]);
1638 b_lds[buf][dq_n][k_base + 5] = __high2half(dq[2]);
1639 b_lds[buf][dq_n][k_base + 6] = __low2half(dq[3]);
1640 b_lds[buf][dq_n][k_base + 7] = __high2half(dq[3]);
1641 } else {
1642 bf162_t dq[4];
1643 dequant_4bit_8_bf16_to_bf16(qa, dq, cf_z, cf_y);
1644 b_lds[buf][dq_n][k_base + 0] = dq[0].x;
1645 b_lds[buf][dq_n][k_base + 1] = dq[0].y;
1646 b_lds[buf][dq_n][k_base + 2] = dq[1].x;
1647 b_lds[buf][dq_n][k_base + 3] = dq[1].y;
1648 b_lds[buf][dq_n][k_base + 4] = dq[2].x;
1649 b_lds[buf][dq_n][k_base + 5] = dq[2].y;
1650 b_lds[buf][dq_n][k_base + 6] = dq[3].x;
1651 b_lds[buf][dq_n][k_base + 7] = dq[3].y;
1652 }
1653 };
1654
1655 dequant_into(0, k_start);
1656 __syncthreads();
1657
1658 int cur_buf = 0;
1659 const int m_row = m_tile + wave_id * 16 + lane_lo;
1660 const T* a_row_ptr = (m_row < size_m) ? (a + m_row * size_k) : nullptr;
1661
1662 for (int k_tile = k_start; k_tile < k_end; k_tile += 16) {
1663 const int next_buf = 1 - cur_buf;
1664 const int k_next = k_tile + 16;
1665
1666 if (k_next < k_end) {
1667 dequant_into(next_buf, k_next);
1668 }
1669
1670 // A-load: vectorized 256-bit.
1671 V16 a_frag, b_frag0, b_frag1, b_frag2, b_frag3;
1672 if (a_row_ptr) {
1673 __builtin_memcpy(&a_frag, a_row_ptr + k_tile, sizeof(a_frag));
1674 } else {
1675 #pragma unroll
1676 for (int i = 0; i < 16; i++) a_frag[i] = (E)0;
1677 }
1678
1679 // Vectorized LDS reads: 32 bytes (16 bf16) per b_frag → ds_load_b128 × 2.
1680 static_assert(sizeof(V16) == 32, "V16 must be 32 bytes for memcpy");
1681 __builtin_memcpy(&b_frag0, &b_lds[cur_buf][lane_lo + 0][0], 32);
1682 __builtin_memcpy(&b_frag1, &b_lds[cur_buf][lane_lo + 16][0], 32);
1683 __builtin_memcpy(&b_frag2, &b_lds[cur_buf][lane_lo + 32][0], 32);
1684 __builtin_memcpy(&b_frag3, &b_lds[cur_buf][lane_lo + 48][0], 32);
1685
1686 c_acc0 = wmma_mma(a_frag, b_frag0, c_acc0);
1687 c_acc1 = wmma_mma(a_frag, b_frag1, c_acc1);
1688 c_acc2 = wmma_mma(a_frag, b_frag2, c_acc2);
1689 c_acc3 = wmma_mma(a_frag, b_frag3, c_acc3);
1690
1691 __syncthreads();
1692 cur_buf = next_buf;
1693 }
1694
1695 // ---- Store C ---- Each wave owns 16M × 64N.
1696 const int m_tile_wave = m_tile + wave_id * 16;
1697 auto store_acc = [&](const v8fp32& acc, int n_base) {
1698 if (gridDim.z > 1) {
1699 const bool is_even_lane = (lane_lo & 1) == 0;
1700 const int out_n_pair = n_base + lane_lo;
1701 #pragma unroll
1702 for (int i = 0; i < 8; i++) {
1703 float other_f = __shfl_xor(acc[i], 1);
1704 if (!is_even_lane) continue;
1705
1706 const int out_m = m_tile_wave + 2 * i + lane_hi;
1707 if (out_m >= size_m || out_n_pair >= size_n) continue;
1708
1709 T* dst = c + out_m * size_n + out_n_pair;
1710 if constexpr (std::is_same<T, half>::value) {
1711 half2 packed =
1712 __halves2half2(__float2half_rn(acc[i]), __float2half_rn(other_f));
1713 atomic_add_pk_f16(reinterpret_cast<half2*>(dst), packed);
1714 } else {
1715 bf162_t packed;
1716 packed.x = __float2bfloat16(acc[i]);
1717 packed.y = __float2bfloat16(other_f);
1718 atomic_add_pk_bf16(reinterpret_cast<bf162_t*>(dst), packed);
1719 }
1720 }
1721 } else {
1722 const int out_n = n_base + lane_lo;
1723 if (out_n >= size_n) return;
1724 #pragma unroll
1725 for (int i = 0; i < 8; i++) {
1726 const int out_m = m_tile_wave + 2 * i + lane_hi;
1727 if (out_m < size_m) {
1728 T* dst = c + out_m * size_n + out_n;
1729 if constexpr (std::is_same<T, half>::value) {
1730 *dst = __float2half_rn(acc[i]);
1731 } else {
1732 *dst = __float2bfloat16(acc[i]);
1733 }
1734 }
1735 }
1736 }
1737 };
1738
1739 store_acc(c_acc0, n_tile + 0);
1740 store_acc(c_acc1, n_tile + 16);
1741 store_acc(c_acc2, n_tile + 32);
1742 store_acc(c_acc3, n_tile + 48);
1743 }
1744
1745 // ===========================================================================
1746 // 128x64_k32 kernel: K=32 per iteration, all 8 waves dequant.
1747 //
1748 // Same 128M × 64N tile as V7, but processes 32 K-elements per iteration
1749 // instead of 16. Halves iteration count and __syncthreads() calls.
1750 //
1751 // Dequant mapping:
1752 // Waves 0-3 dequant N[wave*16 +: 16] × K[0:15] (same as V7)
1753 // Waves 4-7 dequant N[(wave-4)*16 +: 16] × K[16:31] (new)
1754 //
1755 // LDS layout: b_lds[2][64][34] — 2 extra padding elements per row to
1756 // avoid 8-way bank conflicts (row stride 68 bytes gives 16 unique banks
1757 // across the 16 lanes).
1758 //
1759 // Per iteration: 8 WMMAs (2 A-frags × 4 N-groups) vs V7's 4.
1760 // Requires K divisible by 32 and groupsize ≥ 32.
1761 // ===========================================================================
1762 template <typename T>
1763 __global__ void gemm_q4_wmma_kernel_128x64_k32(
1764 const T* __restrict__ a, const uint32_t* __restrict__ b_q,
1765 const uint32_t* __restrict__ b_qzeros, const T* __restrict__ b_scales,
1766 T* __restrict__ c, const int size_m, const int size_n, const int size_k,
1767 const int groups, const int zero_offset) {
1768 using E = typename WmmaNative<T>::elem;
1769 using V16 = typename WmmaNative<T>::v16;
1770
1771 const int m_tile = blockIdx.y * 128;
1772 const int n_tile = blockIdx.x * 64;
1773 if (m_tile >= size_m || n_tile >= size_n) return;
1774
1775 const int tid = threadIdx.x; // 0..255
1776 const int wave_id = tid >> 5; // 0..7
1777 const int lane = tid & 31;
1778 const int lane_lo = lane & 15;
1779 const int lane_hi = lane >> 4;
1780
1781 v8fp32 c_acc0 = {0, 0, 0, 0, 0, 0, 0, 0};
1782 v8fp32 c_acc1 = {0, 0, 0, 0, 0, 0, 0, 0};
1783 v8fp32 c_acc2 = {0, 0, 0, 0, 0, 0, 0, 0};
1784 v8fp32 c_acc3 = {0, 0, 0, 0, 0, 0, 0, 0};
1785
1786 const int groupsize = size_k / groups;
1787
1788 const int k_per_split = size_k / gridDim.z;
1789 const int k_start = blockIdx.z * k_per_split;
1790 const int k_end = k_start + k_per_split;
1791
1792 // Padded LDS: +2 elements per row to break bank conflicts.
1793 // Row stride = 34 elements × 2 bytes = 68 bytes → 16 unique banks.
1794 __shared__ T b_lds[2][64][34];
1795
1796 // All 8 waves dequant. Waves 0-3 fill K[0:15], waves 4-7 fill K[16:31].
1797 const int dq_wave4 = wave_id & 3; // 0-3 for both halves
1798 const int dq_k_half = (wave_id >= 4) ? 1 : 0;
1799 const int dq_n = dq_wave4 * 16 + lane_lo; // N position 0..63
1800 const int dq_an = n_tile + dq_n;
1801 const bool dq_ok = (dq_an < size_n);
1802 const int dq_oct = lane_hi + dq_k_half * 2; // K octet 0-3
1803 const int dq_kb = dq_oct * 8; // K base 0,8,16,24
1804 half2 ch_z = {}, ch_y = {};
1805 float cf_z = 0, cf_y = 0;
1806 int cached_g = -1;
1807
1808 auto dequant_into = [&](int buf, int k_tile) __attribute__((always_inline)) {
1809 if (!dq_ok) return;
1810
1811 const int g = k_tile / groupsize;
1812 if (g != cached_g) {
1813 cached_g = g;
1814 const int qz_idx = g * (size_n / 8) + dq_an / 8;
1815 const uint32_t zero_v = ((b_qzeros[qz_idx] >> ((dq_an & 7) * 4)) & 0xF) +
1816 (uint32_t)zero_offset;
1817 const T sc = b_scales[g * size_n + dq_an];
1818 if constexpr (std::is_same<T, half>::value)
1819 prep_zero_scale_fp16_precise(zero_v, sc, ch_z, ch_y);
1820 else
1821 prep_zero_scale_bf16_f32(zero_v, sc, cf_z, cf_y);
1822 }
1823
1824 const int qk_row = (k_tile / 8) + dq_oct;
1825 const uint32_t qa = b_q[qk_row * size_n + dq_an];
1826
1827 if constexpr (std::is_same<T, half>::value) {
1828 half2 dq[4];
1829 dequant_4bit_8_fp16_precise(qa, dq, ch_z, ch_y);
1830 b_lds[buf][dq_n][dq_kb + 0] = __low2half(dq[0]);
1831 b_lds[buf][dq_n][dq_kb + 1] = __high2half(dq[0]);
1832 b_lds[buf][dq_n][dq_kb + 2] = __low2half(dq[1]);
1833 b_lds[buf][dq_n][dq_kb + 3] = __high2half(dq[1]);
1834 b_lds[buf][dq_n][dq_kb + 4] = __low2half(dq[2]);
1835 b_lds[buf][dq_n][dq_kb + 5] = __high2half(dq[2]);
1836 b_lds[buf][dq_n][dq_kb + 6] = __low2half(dq[3]);
1837 b_lds[buf][dq_n][dq_kb + 7] = __high2half(dq[3]);
1838 } else {
1839 bf162_t dq[4];
1840 dequant_4bit_8_bf16_to_bf16(qa, dq, cf_z, cf_y);
1841 b_lds[buf][dq_n][dq_kb + 0] = dq[0].x;
1842 b_lds[buf][dq_n][dq_kb + 1] = dq[0].y;
1843 b_lds[buf][dq_n][dq_kb + 2] = dq[1].x;
1844 b_lds[buf][dq_n][dq_kb + 3] = dq[1].y;
1845 b_lds[buf][dq_n][dq_kb + 4] = dq[2].x;
1846 b_lds[buf][dq_n][dq_kb + 5] = dq[2].y;
1847 b_lds[buf][dq_n][dq_kb + 6] = dq[3].x;
1848 b_lds[buf][dq_n][dq_kb + 7] = dq[3].y;
1849 }
1850 };
1851
1852 dequant_into(0, k_start);
1853 __syncthreads();
1854
1855 int cur_buf = 0;
1856 const int m_row = m_tile + wave_id * 16 + lane_lo;
1857 const T* a_row_ptr = (m_row < size_m) ? (a + m_row * size_k) : nullptr;
1858
1859 for (int k_tile = k_start; k_tile < k_end; k_tile += 32) {
1860 const int next_buf = 1 - cur_buf;
1861 const int k_next = k_tile + 32;
1862
1863 if (k_next < k_end) {
1864 dequant_into(next_buf, k_next);
1865 }
1866
1867 // A-load: two 16-element fragments for K[0:15] and K[16:31].
1868 V16 a_frag_lo, a_frag_hi;
1869 V16 b_frag0, b_frag1, b_frag2, b_frag3;
1870 if (a_row_ptr) {
1871 __builtin_memcpy(&a_frag_lo, a_row_ptr + k_tile, sizeof(V16));
1872 __builtin_memcpy(&a_frag_hi, a_row_ptr + k_tile + 16, sizeof(V16));
1873 } else {
1874 #pragma unroll
1875 for (int i = 0; i < 16; i++) a_frag_lo[i] = (E)0;
1876 #pragma unroll
1877 for (int i = 0; i < 16; i++) a_frag_hi[i] = (E)0;
1878 }
1879
1880 // --- Lower K half [0:15]: 4 WMMAs ---
1881 static_assert(sizeof(V16) == 32, "V16 must be 32 bytes for memcpy");
1882 __builtin_memcpy(&b_frag0, &b_lds[cur_buf][lane_lo + 0][0], 32);
1883 __builtin_memcpy(&b_frag1, &b_lds[cur_buf][lane_lo + 16][0], 32);
1884 __builtin_memcpy(&b_frag2, &b_lds[cur_buf][lane_lo + 32][0], 32);
1885 __builtin_memcpy(&b_frag3, &b_lds[cur_buf][lane_lo + 48][0], 32);
1886
1887 c_acc0 = wmma_mma(a_frag_lo, b_frag0, c_acc0);
1888 c_acc1 = wmma_mma(a_frag_lo, b_frag1, c_acc1);
1889 c_acc2 = wmma_mma(a_frag_lo, b_frag2, c_acc2);
1890 c_acc3 = wmma_mma(a_frag_lo, b_frag3, c_acc3);
1891
1892 // --- Upper K half [16:31]: 4 WMMAs ---
1893 __builtin_memcpy(&b_frag0, &b_lds[cur_buf][lane_lo + 0][16], 32);
1894 __builtin_memcpy(&b_frag1, &b_lds[cur_buf][lane_lo + 16][16], 32);
1895 __builtin_memcpy(&b_frag2, &b_lds[cur_buf][lane_lo + 32][16], 32);
1896 __builtin_memcpy(&b_frag3, &b_lds[cur_buf][lane_lo + 48][16], 32);
1897
1898 c_acc0 = wmma_mma(a_frag_hi, b_frag0, c_acc0);
1899 c_acc1 = wmma_mma(a_frag_hi, b_frag1, c_acc1);
1900 c_acc2 = wmma_mma(a_frag_hi, b_frag2, c_acc2);
1901 c_acc3 = wmma_mma(a_frag_hi, b_frag3, c_acc3);
1902
1903 __syncthreads();
1904 cur_buf = next_buf;
1905 }
1906
1907 // ---- Store C ---- Same as V7.
1908 const int m_tile_wave = m_tile + wave_id * 16;
1909 auto store_acc = [&](const v8fp32& acc, int n_base) {
1910 if (gridDim.z > 1) {
1911 const bool is_even_lane = (lane_lo & 1) == 0;
1912 const int out_n_pair = n_base + lane_lo;
1913 #pragma unroll
1914 for (int i = 0; i < 8; i++) {
1915 float other_f = __shfl_xor(acc[i], 1);
1916 if (!is_even_lane) continue;
1917
1918 const int out_m = m_tile_wave + 2 * i + lane_hi;
1919 if (out_m >= size_m || out_n_pair >= size_n) continue;
1920
1921 T* dst = c + out_m * size_n + out_n_pair;
1922 if constexpr (std::is_same<T, half>::value) {
1923 half2 packed =
1924 __halves2half2(__float2half_rn(acc[i]), __float2half_rn(other_f));
1925 atomic_add_pk_f16(reinterpret_cast<half2*>(dst), packed);
1926 } else {
1927 bf162_t packed;
1928 packed.x = __float2bfloat16(acc[i]);
1929 packed.y = __float2bfloat16(other_f);
1930 atomic_add_pk_bf16(reinterpret_cast<bf162_t*>(dst), packed);
1931 }
1932 }
1933 } else {
1934 const int out_n = n_base + lane_lo;
1935 if (out_n >= size_n) return;
1936 #pragma unroll
1937 for (int i = 0; i < 8; i++) {
1938 const int out_m = m_tile_wave + 2 * i + lane_hi;
1939 if (out_m < size_m) {
1940 T* dst = c + out_m * size_n + out_n;
1941 if constexpr (std::is_same<T, half>::value) {
1942 *dst = __float2half_rn(acc[i]);
1943 } else {
1944 *dst = __float2bfloat16(acc[i]);
1945 }
1946 }
1947 }
1948 }
1949 };
1950
1951 store_acc(c_acc0, n_tile + 0);
1952 store_acc(c_acc1, n_tile + 16);
1953 store_acc(c_acc2, n_tile + 32);
1954 store_acc(c_acc3, n_tile + 48);
1955 }
1956
1957 #else // non-RDNA3 device pass: empty kernels for symbol parity (covers the
1958 // three kernels that share this launcher).
1959 template <typename T>
1960 __global__ void gemm_q4_wmma_kernel_64x64_4w(const T*, const uint32_t*,
1961 const uint32_t*, const T*, T*,
1962 const int, const int, const int,
1963 const int, const int) {}
1964 template <typename T>
1965 __global__ void gemm_q4_wmma_kernel_128x64_k16(const T*, const uint32_t*,
1966 const uint32_t*, const T*, T*,
1967 const int, const int, const int,
1968 const int, const int) {}
1969 template <typename T>
1970 __global__ void gemm_q4_wmma_kernel_128x64_k32(const T*, const uint32_t*,
1971 const uint32_t*, const T*, T*,
1972 const int, const int, const int,
1973 const int, const int) {}
1974 #endif
1975
1976 template <typename T>
1977 void launch_gemm_q4_wmma_64x64_4w(const T* a, const uint32_t* b_q_weight,
1978 const uint32_t* b_qzeros, const T* b_scales,
1979 T* c, int size_m, int size_n, int size_k,
1980 int groups, int zero_offset,
1981 cudaStream_t stream) {
1982 // Fall back to v4 when N < 64 (would waste 1+ waves on out-of-range cols).
1983 if (size_m < 64 || size_n < 64) {
1984 launch_gemm_q4_wmma_64x32_4w<T>(a, b_q_weight, b_qzeros, b_scales, c,
1985 size_m, size_n, size_k, groups, zero_offset,
1986 stream);
1987 return;
1988 }
1989
1990 // V8 (128M × 64N, K=32/iter, 8-wave dequant) when K%32==0 and gs≥32.
1991 // Falls back to V7 otherwise.
1992 if (size_m >= 128) {
1993 const int k_split =
1994 compute_wmma_k_split_mn(size_m, size_n, size_k, 128, 64);
1995 const int groupsize = size_k / groups;
1996 dim3 block(256);
1997 dim3 grid((size_n + 63) / 64, (size_m + 127) / 128, k_split);
1998 if (size_k % 32 == 0 && groupsize >= 32 && (size_k / k_split) % 32 == 0) {
1999 gemm_q4_wmma_kernel_128x64_k32<T><<<grid, block, 0, stream>>>(
2000 a, b_q_weight, b_qzeros, b_scales, c, size_m, size_n, size_k, groups,
2001 zero_offset);
2002 } else {
2003 gemm_q4_wmma_kernel_128x64_k16<T><<<grid, block, 0, stream>>>(
2004 a, b_q_weight, b_qzeros, b_scales, c, size_m, size_n, size_k, groups,
2005 zero_offset);
2006 }
2007 return;
2008 }
2009
2010 // 4 waves per block (128 threads), 64M × 64N tile per block.
2011 const int k_split = compute_wmma_k_split_mn(size_m, size_n, size_k, 64, 64);
2012 dim3 block(128);
2013 dim3 grid((size_n + 63) / 64, (size_m + 63) / 64, k_split);
2014 gemm_q4_wmma_kernel_64x64_4w<T>
2015 <<<grid, block, 0, stream>>>(a, b_q_weight, b_qzeros, b_scales, c, size_m,
2016 size_n, size_k, groups, zero_offset);
2017 }
2018
2019 } // namespace gptq_rdna3_wmma
2020 } // namespace vllm
2021
2022 // ---------------------------------------------------------------------------
2023 // Public entry point.
2024 // ---------------------------------------------------------------------------
2025 //
2026 // Inputs:
2027 // a [M, K] half or bfloat16
2028 // b_q_weight[K/8, N] uint32 (already shuffled via gptq_shuffle)
2029 // b_qzeros [groups, N/8] uint32 (packed 4-bit zeros)
2030 // b_scales [groups, N] half or bfloat16
2031 // use_v2_format bool (true = GPTQv2, no +1 zero offset)
2032 //
2033 // Output:
2034 // c [M, N] same dtype as a
2035 //
2036 // Requirements:
2037 // * size_m >= 16 (otherwise prefer the scalar gptq_gemm_rdna3 op)
2038 // * size_n % 16 == 0 (WMMA tile size)
2039 // * size_k % 16 == 0 (WMMA tile size)
2040
2041 torch::Tensor gptq_gemm_rdna3_wmma(torch::Tensor a, torch::Tensor b_q_weight,
2042 torch::Tensor b_qzeros,
2043 torch::Tensor b_scales, bool use_v2_format) {
2044 TORCH_CHECK(a.is_cuda(), "a must be a CUDA/HIP tensor");
2045 TORCH_CHECK(b_q_weight.is_cuda(), "b_q_weight must be a CUDA/HIP tensor");
2046 TORCH_CHECK(b_qzeros.is_cuda(), "b_qzeros must be a CUDA/HIP tensor");
2047 TORCH_CHECK(b_scales.is_cuda(), "b_scales must be a CUDA/HIP tensor");
2048 TORCH_CHECK(a.dim() == 2, "a must be 2D [M, K]");
2049 TORCH_CHECK(b_q_weight.dim() == 2, "b_q_weight must be 2D [K/8, N]");
2050 TORCH_CHECK(
2051 a.scalar_type() == torch::kHalf || a.scalar_type() == torch::kBFloat16,
2052 "a must be half or bfloat16");
2053 TORCH_CHECK(a.scalar_type() == b_scales.scalar_type(),
2054 "b_scales dtype must match a");
2055
2056 const at::cuda::OptionalCUDAGuard device_guard(device_of(a));
2057 auto stream = at::cuda::getCurrentCUDAStream();
2058
2059 int size_m = (int)a.size(0);
2060 int size_k = (int)a.size(1);
2061 int size_n = (int)b_q_weight.size(1);
2062 int groups = (int)b_qzeros.size(0);
2063
2064 TORCH_CHECK(b_q_weight.size(0) * 8 == size_k,
2065 "b_q_weight first dim must be K/8");
2066 TORCH_CHECK(b_scales.size(0) == groups,
2067 "b_scales must have same group count as qzeros");
2068 TORCH_CHECK(b_scales.size(1) == size_n, "b_scales last dim must be N");
2069 TORCH_CHECK(size_n % 16 == 0, "WMMA path requires N % 16 == 0");
2070 TORCH_CHECK(size_k % 16 == 0, "WMMA path requires K % 16 == 0");
2071
2072 auto opts = torch::TensorOptions().dtype(a.dtype()).device(a.device());
2073 // Always zero-init the output: some V3-V8 boundary threads may exit
2074 // without writing their output cell (e.g. out_m >= size_m), leaving
2075 // uninitialized garbage when torch::empty is used. The cost is
2076 // negligible (< 1.5% of prefill time on gfx1100).
2077 at::Tensor c = torch::zeros({size_m, size_n}, opts);
2078
2079 const int zero_offset = use_v2_format ? 0 : 1;
2080
2081 // launch_gemm_q4_wmma_64x64_4w dispatches:
2082 // M >= 128 → 128x64_k32 / 128x64_k16 (8 waves, K=32/16 per iter)
2083 // 64 <= M < 128 & N >= 64 → 64x64_4w (4 waves, 4 wmma/wave/K-iter)
2084 // M >= 64 && 32 <= N < 64 → 64x32_4w (4 waves, 2 wmma/wave/K-iter)
2085 // M >= 64 && N < 32 → 64x16_4w (4 waves)
2086 // 32 <= M < 64 → 32x16_2w (2 waves)
2087 // M < 32 → 16x16_1w (1 wave)
2088 if (a.scalar_type() == torch::kHalf) {
2089 vllm::gptq_rdna3_wmma::launch_gemm_q4_wmma_64x64_4w<half>(
2090 (const half*)a.data_ptr(), (const uint32_t*)b_q_weight.data_ptr(),
2091 (const uint32_t*)b_qzeros.data_ptr(), (const half*)b_scales.data_ptr(),
2092 (half*)c.data_ptr(), size_m, size_n, size_k, groups, zero_offset,
2093 stream);
2094 } else {
2095 vllm::gptq_rdna3_wmma::launch_gemm_q4_wmma_64x64_4w<
2096 vllm::gptq_rdna3_wmma::bf16_t>(
2097 (const vllm::gptq_rdna3_wmma::bf16_t*)a.data_ptr(),
2098 (const uint32_t*)b_q_weight.data_ptr(),
2099 (const uint32_t*)b_qzeros.data_ptr(),
2100 (const vllm::gptq_rdna3_wmma::bf16_t*)b_scales.data_ptr(),
2101 (vllm::gptq_rdna3_wmma::bf16_t*)c.data_ptr(), size_m, size_n, size_k,
2102 groups, zero_offset, stream);
2103 }
2104
2105 return c;
2106 }