|
| 1 | +# SPDX-License-Identifier: MIT |
| 2 | +# Copyright (C) 2024-2026, Advanced Micro Devices, Inc. All rights reserved. |
| 3 | +"""Hand-written HIP prefill-phase FP8 MQA indexer logits kernel for gfx950. |
| 4 | +
|
| 5 | +The dense half of the DeepSeek-V3.2 / GLM-5 sparse-attention lightning indexer: |
| 6 | +
|
| 7 | + logits[m, n] = sum_h relu(Q[m,h,:] . K[n,:]) * w[m,h] * kv_scale[n] |
| 8 | +
|
| 9 | +K has already been gathered into a contiguous ``[N, 128]`` buffer, so there is no |
| 10 | +block table. Drop-in for ``aiter.ops.triton.attention.fp8_mqa_logits``, so the two |
| 11 | +can be A/B'd on identical inputs. |
| 12 | +
|
| 13 | +Fixed at n_heads=32, head_dim=128 -- the shipped GLM-5-FP8 indexer shape. |
| 14 | +``is_supported()`` gates on that, so a caller that also has to serve other shapes |
| 15 | +can route them to the Triton kernel rather than trip a TORCH_CHECK. |
| 16 | +""" |
| 17 | + |
| 18 | +from torch import Tensor |
| 19 | + |
| 20 | +from ..jit.core import compile_ops |
| 21 | +from ..jit.utils.chip_info import get_gfx |
| 22 | + |
| 23 | +MD_NAME = "module_fp8_mqa_logits" |
| 24 | + |
| 25 | +SUPPORTED_GFX = ("gfx950",) |
| 26 | +NUM_HEADS = 32 |
| 27 | +HEAD_DIM = 128 |
| 28 | + |
| 29 | + |
| 30 | +def is_supported(num_heads: int, head_dim: int) -> bool: |
| 31 | + """True when this kernel can run this shape on this device.""" |
| 32 | + return get_gfx() in SUPPORTED_GFX and num_heads == NUM_HEADS and head_dim == HEAD_DIM |
| 33 | + |
| 34 | + |
| 35 | +@compile_ops(MD_NAME, fc_name="fp8_mqa_logits") |
| 36 | +def fp8_mqa_logits( |
| 37 | + q_fp8: Tensor, |
| 38 | + k_fp8: Tensor, |
| 39 | + kv_scale: Tensor, |
| 40 | + weights: Tensor, |
| 41 | + cu_seqlen_ks: Tensor, |
| 42 | + cu_seqlen_ke: Tensor, |
| 43 | + BlockM: int = 0, |
| 44 | + SplitN: int = 0, |
| 45 | + num_warps: int = 0, |
| 46 | + TotalCuCount: int = 256, |
| 47 | + clean_logits: bool = True, |
| 48 | + unroll2: int = -1, |
| 49 | + reverse_rows: int = -1, |
| 50 | + out: Tensor | None = None, |
| 51 | +) -> Tensor: |
| 52 | + """Prefill indexer logits over a contiguous K buffer. |
| 53 | +
|
| 54 | + q_fp8 [M, 32, 128] fp8 k_fp8 [N, 128] fp8 |
| 55 | + kv_scale [N] f32 weights [M, 32] f32 |
| 56 | + cu_seqlen_ks/ke [M] i32 -- row m is valid on [ks[m], ke[m]). Either bound may |
| 57 | + legally sit outside [0, N); the row is then empty over the part that does. |
| 58 | +
|
| 59 | + The zero-valued tunables (BlockM, SplitN, num_warps) mean "use the host |
| 60 | + heuristic"; unroll2 and reverse_rows are tri-state, -1 for the heuristic and |
| 61 | + 0/1 to force off/on. Writes into `out` when given (and returns it), otherwise allocates |
| 62 | + [M, N] f32; outside each row's window the kernel writes -inf when clean_logits, |
| 63 | + and leaves the buffer untouched otherwise. |
| 64 | + """ |
| 65 | + ... |
0 commit comments