#include "kittens.cuh" using namespace kittens; #ifndef ROUTER_M #define ROUTER_M 16384 #endif #ifndef ROUTER_K #define ROUTER_K 2880 #endif #ifndef ROUTER_E #define ROUTER_E 32 #endif constexpr int BLOCK_M = 64; constexpr int BLOCK_K = 64; constexpr int NUM_WARPS = 4; constexpr int THREADS = NUM_WARPS * WARP_THREADS; using G = kittens::group; using XST = st_bf; using WST = st_bf; using XRT = rt_bf<16, BLOCK_K, row_l, rt_16x32_s>; using WRT = rt_bf; using CRT = rt_fl<16, ROUTER_E, col_l, rt_16x16_s>; static_assert(ROUTER_M % BLOCK_M == 0, "ROUTER_M must be divisible by 64"); static_assert(ROUTER_K % BLOCK_K == 0, "ROUTER_K must be divisible by 64"); static_assert(ROUTER_E == 32, "the small-N tile is specialized for 32 experts"); extern "C" __global__ __launch_bounds__(THREADS, 4) void moe_router_mfma( float *__restrict__ out, bf16 *__restrict__ x_ptr, bf16 *__restrict__ weight_ptr, bf16 *__restrict__ bias) { gl X{x_ptr, nullptr, nullptr, nullptr, nullptr}; gl W{weight_ptr, nullptr, nullptr, nullptr, nullptr}; __shared__ XST Xs[2]; __shared__ WST Ws[2]; XRT xr; WRT wr; CRT accum; zero(accum); const int block_m = __builtin_amdgcn_workgroup_id_x(); const int warp_m = warpid(); // Prefetch the next tile into alternate LDS storage without changing the MFMA accumulation order. G::load(Xs[0], X, {0, 0, block_m, 0}); G::load(Ws[0], W, {0, 0, 0, 0}); asm volatile("s_waitcnt vmcnt(0)"); asm volatile("s_waitcnt lgkmcnt(0)"); __builtin_amdgcn_s_barrier(); #pragma unroll for (int kk = 0; kk < ROUTER_K / BLOCK_K; kk++) { const int cur = kk & 1; const int next = cur ^ 1; load(xr, subtile_inplace<16, BLOCK_K>(Xs[cur], {warp_m, 0})); load(wr, subtile_inplace(Ws[cur], {0, 0})); asm volatile("s_waitcnt lgkmcnt(0)"); if (kk + 1 < ROUTER_K / BLOCK_K) { G::load(Xs[next], X, {0, 0, block_m, kk + 1}); G::load(Ws[next], W, {0, 0, 0, kk + 1}); } __builtin_amdgcn_s_setprio(1); mma_ABt(accum, xr, wr, accum); __builtin_amdgcn_s_setprio(0); __builtin_amdgcn_sched_barrier(0); if (kk + 1 < ROUTER_K / BLOCK_K) { asm volatile("s_waitcnt vmcnt(0)"); asm volatile("s_waitcnt lgkmcnt(0)"); __builtin_amdgcn_s_barrier(); } } // A 16x16 MFMA accumulator is column-layout: each lane owns four consecutive rows // at one column. Store all 64x32 FP32 results directly; no padded or undersized output ABI. const int lane = laneid(); const int row0 = block_m * BLOCK_M + warp_m * 16 + 4 * (lane / 16); const int lane_col = lane % 16; #pragma unroll for (int j = 0; j < ROUTER_E / 16; j++) { const int col = j * 16 + lane_col; const float b = (float)bias[col]; const float vals[4] = {accum.tiles[0][j].data[0].x, accum.tiles[0][j].data[0].y, accum.tiles[0][j].data[1].x, accum.tiles[0][j].data[1].y}; #pragma unroll for (int r = 0; r < 4; r++) out[(long long)(row0 + r) * ROUTER_E + col] = vals[r] + b; } }