Skip to content

[Dialect][Kernel][Perf] Add gfx120x.wmma atom and port the RDNA4 GEMM to the layout API - #943

Open
vlluvia wants to merge 1 commit into
ROCm:mainfrom
vlluvia:rdna4-wmma-atom
Open

[Dialect][Kernel][Perf] Add gfx120x.wmma atom and port the RDNA4 GEMM to the layout API#943
vlluvia wants to merge 1 commit into
ROCm:mainfrom
vlluvia:rdna4-wmma-atom

Conversation

@vlluvia

@vlluvia vlluvia commented Jul 31, 2026

Copy link
Copy Markdown

RDNA4 (gfx1200 / gfx1201) has no usable WMMA atom today: fx.rocdl.WMMA() routed every gfx12* target to MmaOpGFX1250_WMMAType, whose verifier only accepts K=32 on the f16/bf16 paths, while RDNA4 only has the 16x16x16 shapes. The RDNA4 GEMM therefore had to spell the v8 fragment layout out as hand-written lane arithmetic instead of deriving it from an atom.

Add a dedicated gfx12.wmma atom. RDNA4 sits between the two existing WMMA atoms: it keeps the gfx11 16x16x16 instruction shapes and the 3-operand ROCDL intrinsic form, but uses the gfx1250 v8 register ABI, so the fragment layouts are shared with the gfx1250 helpers and only codegen is new. Fix the dispatch order in fx.rocdl.WMMA() accordingly -- gfx1250 must be matched before the gfx12 prefix would otherwise claim it.

With the atom in place, rewrite kernels/gemm/rdna_f16_gemm.py against make_tiled_copy_{A,B,C} and the tiled_mma thread slices (348 -> 281 lines). Selecting an LDS partition needs a compile-time constant stage index, so the k-loop now processes two tiles per iteration with the odd/even remainder peeled.

Output is bit-identical to the previous kernel (torch.equal) at 2048^3, 4096^3 and 8192^3 for both bf16 and f16, and it is 5-10% faster (geomean 1.08x on gfx1201). The win is not from doing less work: per k-tile the ISA still has 32 v_wmma, 16 ds_load_b128, 8 ds_store_b128 and 8 buffer_load_b128. Making the stage index constant folds the LDS addresses into ds_load immediate offsets, which cuts per-k-tile address VALU from 22 to 3 ops and frees enough registers to keep 24 fragment quads live instead of 6. The old loop had to drain LDS (s_wait_dscnt 0x0) 9 times per k-tile to recycle those registers; the new one drains 1.5 times and issues runs of up to 16 WMMA with no wait in between. VGPR use rises 189 -> 247, which costs nothing here because the 40KB LDS footprint already caps the CU at one workgroup.

image

Motivation

Technical Details

Test Plan

Test Result

Submission Checklist

@coderfeli
coderfeli requested a review from sjfeng1999 July 31, 2026 06:13
// gfx1250 is a different target: it has 16x16x32 and the mods/reuse operand
// form, so it keeps its own `gfx1250.wmma` atom.

def FlyROCDL_MmaOpGFX12_WMMA : FlyROCDL_MmaOp<"MmaOpGFX12_WMMA", "gfx12.wmma", []> {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If RDNA4 includes only GFX1200 and GFX1201. GFX120X_WMMA would be a better name because it distinguishes cleanly from GFX1250, The same applies elsewhere.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to GFX120X throughout — type, mnemonic, directory, test files and diagnostics. Also narrowed the dispatch guard in fx.rocdl.WMMA() from gfx12 to gfx120 for the same reason, so it no longer overlaps gfx1250.

Comment thread kernels/gemm/rdna_f16_gemm.py Outdated
Comment on lines +268 to +270
arg_a_2d = fx.Tensor(fx.make_view(fx.get_iter(arg_a), fx.make_layout((M, K), (K, 1))))
arg_bt_2d = fx.Tensor(fx.make_view(fx.get_iter(arg_bt), fx.make_layout((N, K), (K, 1))))
arg_c_2d = fx.Tensor(fx.make_view(fx.get_iter(arg_c), fx.make_layout((M, N), (N, 1))))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fx.Tensor wrapper is unnecessary actually here .

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, dropped.

@sjfeng1999 sjfeng1999 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything else looks great.

RDNA4 (gfx1200 / gfx1201) has no usable WMMA atom today: fx.rocdl.WMMA()
routed every gfx12* target to MmaOpGFX1250_WMMAType, whose verifier only
accepts K=32 on the f16/bf16 paths, while RDNA4 only has the 16x16x16
shapes. The RDNA4 GEMM therefore had to spell the v8 fragment layout out as
hand-written lane arithmetic instead of deriving it from an atom.

Add a dedicated gfx120x.wmma atom. RDNA4 sits between the two existing WMMA
atoms: it keeps the gfx11 16x16x16 instruction shapes and the 3-operand
ROCDL intrinsic form, but uses the gfx1250 v8 register ABI, so the fragment
layouts are shared with the gfx1250 helpers and only codegen is new. The name
is gfx120x rather than gfx12 because gfx1250 is also a gfx12* target but keeps
its own atom; fx.rocdl.WMMA() matches RDNA4 on the correspondingly narrowed
gfx120 prefix, which is disjoint from gfx1250.

With the atom in place, rewrite kernels/gemm/rdna_f16_gemm.py against
make_tiled_copy_{A,B,C} and the tiled_mma thread slices (348 -> 281 lines).
Selecting an LDS partition needs a compile-time constant stage index, so the
k-loop now processes two tiles per iteration with the odd/even remainder
peeled.

Output is bit-identical to the previous kernel (torch.equal) at 2048^3,
4096^3 and 8192^3 for both bf16 and f16, and it is 5-10% faster (geomean
1.08x on gfx1201). The win is not from doing less work: per k-tile the ISA
still has 32 v_wmma, 16 ds_load_b128, 8 ds_store_b128 and 8
buffer_load_b128. Making the stage index constant folds the LDS addresses
into ds_load immediate offsets, which cuts per-k-tile address VALU from 22
to 3 ops and frees enough registers to keep 24 fragment quads live instead
of 6. The old loop had to drain LDS (s_wait_dscnt 0x0) 9 times per k-tile to
recycle those registers; the new one drains 1.5 times and issues runs of up
to 16 WMMA with no wait in between. VGPR use rises 189 -> 247, which costs
nothing here because the 40KB LDS footprint already caps the CU at one
workgroup.

Signed-off-by: vlluvia <925716329@qq.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@vlluvia vlluvia changed the title [Dialect][Kernel][Perf] Add gfx12.wmma atom and port the RDNA4 GEMM to the layout API [Dialect][Kernel][Perf] Add gfx120x.wmma atom and port the RDNA4 GEMM to the layout API Jul 31, 2026
@vivienfanghuagood

Copy link
Copy Markdown
Collaborator

good job!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants