Files
TheRock/rocm-libraries/dnn-providers/hip-kernel-provider/rocke/library/kernels/common/attention_arch.py
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

146 lines
6.9 KiB
Python

# Copyright (c) Advanced Micro Devices, Inc., or its affiliates.
# SPDX-License-Identifier: MIT
"""Architecture gating for the tiled MFMA unified-attention kernels.
There are two arch-specific tiled-attention builders, and this module decides
which targets each may run on:
* **gfx950 (CDNA4)** — ``instances/gfx950/attention_tiled_2d.py`` is built
around two gfx950 ISA features: the **wide-K MFMA atoms**
``mfma_f32_16x16x32_{f16,bf16}`` / ``mfma_f32_32x32x16_{f16,bf16}`` (the QK
and PV matmuls use the wide-K form; K-step 32 / 16 head-dim elements per
atom) and the **LDS transpose reads** ``ds_read_b64_tr_b16`` /
``ds_read_tr_b8`` (the PV ``B`` operand). Requesting either on a target that
lacks it makes the AMDGPU backend abort with ``LLVM ERROR: Cannot select
intrinsic`` -- a hard process crash, not a recoverable Python error.
* **gfx942 (CDNA3)** — ``instances/gfx942/attention_tiled_2d.py`` is the
narrow-atom variant: QK and PV both use the ``16x16x16`` f16/bf16 atom
(K-step 16), and the V ``B`` operand is built from ordinary strided LDS
loads, so it needs neither the wide-K atoms nor ``ds_read_*_tr_*``.
Rather than let comgr crash the whole process when a caller asks for a target
that has no matching path, the builders validate up front and raise a clean
structured error. Routing an admitted arch to the correct builder is the
caller's job (``instances/common/attention_unified.py:_tiled_2d_impl``);
admitting gfx942 here without that routing would drive the gfx950 builder onto
gfx942 ISA and crash comgr -- so the routing seam and this relaxation land
together.
The helpers here are deliberately catalog-driven (they query
:class:`rocke.core.arch.ArchTarget`) so a future architecture lights up the
matching path automatically. The one wrinkle is that the MFMA catalog lists the
``32x32x16`` bf16 atom as absent even on gfx950 (it compiles fine in practice),
so the wide-K check folds in a ``has_ds_read_tr`` cross-check: a target that
advertises the transpose-read family is taken to have the wide-K atoms too.
"""
from __future__ import annotations
from typing import Tuple
from rocke.core.arch import ArchTarget
def _wide_k_mfma_available(target: ArchTarget) -> bool:
"""True iff this target has the wide-K (K=32 / 32x32x16) f16/bf16 MFMA.
Sourced from the MMA catalog (``16x16x32`` f16 is the canonical wide-K
marker and is correctly reported per arch), with a transpose-read
cross-check so a target that advertises the gfx950 ``ds_read_*_tr_*``
family is always treated as wide-K capable even where the catalog is
incomplete (the ``32x32x16`` bf16 atom).
"""
if target.mma.has_shape(
a_dtype="f16", b_dtype="f16", c_dtype="fp32", m=16, n=16, k=32
):
return True
return bool(target.memory.has_ds_read_tr)
# Arches that have a dedicated narrow-atom / strided-V tiled-2D variant routed
# by ``instances/common/attention_unified._tiled_2d_impl``. Only these may be
# admitted on the narrow path by ``validate_tiled_attention_arch`` -- an arch
# that is narrow-atom-capable but NOT listed here has no routed builder and
# would fall through to the gfx950 builder (wide-K / ds_read_tr ISA) and crash
# comgr. Keep this in lockstep with ``_tiled_2d_impl``.
_NARROW_TILED_2D_ARCHES = frozenset({"gfx942"})
def _narrow_k_mfma_available(target: ArchTarget) -> bool:
"""True iff this target has the narrow ``16x16x16`` f16 AND bf16 MFMA.
This is the atom the gfx942 (CDNA3) tiled-2D variant
(``instances/gfx942/attention_tiled_2d.py``) is built on: QK and PV both
use ``16x16x16`` with a K-step of 16, and the V ``B`` operand is built from
ordinary strided LDS loads instead of ``ds_read_*_tr_*``. Both f16 and bf16
must be present (bf16 lowers through the ``_1k`` intrinsic).
"""
return target.mma.has_shape(
a_dtype="f16", b_dtype="f16", c_dtype="fp32", m=16, n=16, k=16
) and target.mma.has_shape(
a_dtype="bf16", b_dtype="bf16", c_dtype="fp32", m=16, n=16, k=16
)
def validate_tiled_attention_arch(arch: str) -> Tuple[bool, str]:
"""Return ``(ok, reason)`` for running a tiled attention kernel on ``arch``.
Two arch families are admitted:
* **gfx950 (CDNA4)** — the wide-K MFMA atoms (``mfma_f32_16x16x32`` /
``mfma_f32_32x32x16``) AND LDS transpose reads (``ds_read_b64_tr_b16``).
This is the original gfx950 kernel's requirement, preserved unchanged.
* **gfx942 (CDNA3)** — the narrow ``16x16x16`` f16/bf16 atom, with the V
``B`` operand built from strided LDS loads (no transpose read required).
The dedicated ``instances/gfx942`` variant runs this path, so gfx942 does
NOT need wide-K or ``has_ds_read_tr``.
This predicate lets the selector / dispatcher drop a target that has neither
path with a structured reason instead of letting comgr abort the process at
lower time. Routing the admitted arch to the matching builder is the
caller's responsibility (``_tiled_2d_impl(arch)``); admitting gfx942 here
without that routing would drive the gfx950 builder onto gfx942 ISA.
"""
try:
target = ArchTarget.from_gfx(arch)
except KeyError as e:
return False, str(e)
# gfx950 wide-K + transpose-read path (unchanged).
if _wide_k_mfma_available(target) and target.memory.has_ds_read_tr:
return True, "ok"
# Narrow-atom + strided-V path. Admission is restricted to arches that
# ``_tiled_2d_impl`` actually routes to a dedicated narrow variant -- only
# gfx942 today. Admitting any narrow-atom-capable arch here would be unsafe:
# an arch without a routed narrow builder (e.g. gfx90a) would fall through
# ``_tiled_2d_impl`` to the gfx950 builder and emit wide-K / ds_read_tr ISA
# it cannot run, crashing comgr. Adding another narrow arch means extending
# BOTH this set and ``_tiled_2d_impl``.
if arch in _NARROW_TILED_2D_ARCHES and _narrow_k_mfma_available(target):
return True, "ok"
if not _wide_k_mfma_available(target):
return (
False,
f"tiled attention requires either the wide-K MFMA atoms "
f"(mfma_f32_16x16x32 / mfma_f32_32x32x16, gfx950) or, on a narrow "
f"variant arch ({', '.join(sorted(_NARROW_TILED_2D_ARCHES))}), the "
f"16x16x16 f16/bf16 atom; neither path is available on {arch}",
)
return (
False,
f"tiled attention requires LDS transpose reads "
f"(ds_read_b64_tr_b16) for the wide-K path, absent on {arch}",
)
def require_tiled_attention_arch(arch: str) -> None:
"""Raise :class:`NotImplementedError` if ``arch`` cannot run the kernel.
Called at the very top of the tiled attention builders so a gfx942
request fails with a clean Python error *before* any IR is emitted and
long before comgr would hit ``LLVM ERROR: Cannot select intrinsic``.
"""
ok, reason = validate_tiled_attention_arch(arch)
if not ok:
raise NotImplementedError(reason)