Skip to content

[Test][gfx1250] State the FP8 GEMM shape contract and check that stores stay inside N - #1229

Closed
jhinpan wants to merge 4 commits into
mainfrom
fix/gfx1250-a8w8-n-tail
Closed

jhinpan wants to merge 4 commits into
mainfrom
fix/gfx1250-a8w8-n-tail

Conversation

@jhinpan

@jhinpan jhinpan commented Oct 6, 2026 •

Copy link
Copy Markdown
Collaborator

launch_gemm_a8w8 (the gfx1250 FP8 GEMM) requires N % tile_n == 0; the caller pads N to a multiple of tile_n. This PR states the shape contract in the launcher docstring: N % tile_n == 0, K % tile_k == 0 and K // tile_k >= num_buffers - 1 (blockscale also N % 128 == 0). A partial last M tile is clamped.

The tests keep skipping shapes that break the contract. For the cases with a padded C (ldc > N), _run_and_check now fills the columns past N with a signaling-NaN bit pattern before the launch and checks that the bits do not change, so a store past N fails the test, even one that stores a NaN.

No kernel change.

launch_gemm_a8w8 launches ceil(N / tile_n) N-tiles, but the B copy and
the C store of the last tile were not bounded in N. When N % tile_n != 0,
the last tile read B rows past N and stored all tile_n columns from its
start. With ldc == N, the columns past N landed at the start of the next
row of C, and past the end of C for the last row. The upstream tests
only ran shapes with N % tile_n == 0.

Bound the B copy (in 16-row blocks) and the C store by the columns left
from the tile start: the TDM zero-fills B rows past N and drops C
columns past N. The blockscale B-scale copy is now bounded for every
tile size, since a 256-wide tail tile would otherwise read past the
scales. N must stay a multiple of 16 (B is preshuffled in 16-row
blocks), and blockscale still needs N % 128 == 0.

Tests add per-token/per-channel cases with N = 2112 (the DeepSeek-V3
fused qkv_a width) and a blockscale case with N = 1152 and 256-wide
tiles; the checks reject N % 16 != 0 instead of N % tile_n != 0.

Signed-off-by: Jin Pan <jin.pan@amd.com>
With ldc == N, the N-tail cases failed on main only when a stray tail
store landed after the owner tile's store. The test runner now fills
the C padding with NaN and asserts that it stays NaN, and two padded
N-tail cases put every stray column into the padding, so they fail on
main on every run. The launcher's docstring states its N and K
requirements.

Signed-off-by: Jin Pan <jin.pan@amd.com>
@jhinpan jhinpan changed the title [Fix][gfx1250] Keep the FP8 GEMM's B reads and C stores inside N [Bugfix][gfx1250] Keep the FP8 GEMM's B reads and C stores inside N Oct 7, 2026
The padding check filled the columns past N with NaN and asserted that
they were still NaN, so a stray store of a NaN passed. The stray
columns of main are computed from B rows past N, whose contents are
undefined, so such a store can be a NaN.

Fill the padding with a signaling-NaN bit pattern that no computed
output carries and compare the raw bits.

Signed-off-by: Jin Pan <jin.pan@amd.com>

@aoli26 aoli26 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

@zhiding512

Copy link
Copy Markdown
Collaborator

The bad results in the summary are shapes this kernel does not support. The tests skip N % tile_n != 0 with "no silent pad", and shapes that meet that requirement already match the reference on main.

launch_gemm_a8w8 is a tiled kernel. N is already required to be a multiple of 16 (128 in blockscale); padding N up to tile_n belongs with the caller. 2112 is a real width, and the caller can pad it (2112 → 2304 for a 256-wide tile) or select a tile that divides it (64 does).

Clamping B and C in the descriptor changes that contract, and #1228 / #1233 then have to apply the same bound again in store_c. Please keep the N % tile_n == 0 requirement and the test skip, and leave the tail to the caller. A launch-time check is enough if the concern is a missed pad silently writing past N.

Per review, the caller pads N to a multiple of tile_n. Revert the B and C
clamps and the N-tail test cases, and restore the test skip. The launcher
docstring now states the shape contract, and _run_and_check still checks
that no store reaches the C columns past N.

Signed-off-by: Jin Pan <jin.pan@amd.com>
@jhinpan jhinpan changed the title [Bugfix][gfx1250] Keep the FP8 GEMM's B reads and C stores inside N [Test][gfx1250] State the FP8 GEMM shape contract and check that stores stay inside N Oct 11, 2026
@jhinpan

jhinpan commented Oct 11, 2026

Copy link
Copy Markdown
Collaborator Author

Done in 2fd1236: the B and C clamps and the N-tail cases are gone, and the N % tile_n == 0 skip is back. What stays is the launcher docstring, which now states the shape contract, including that the caller pads N, and the check in _run_and_check that no store reaches the C columns past N in the padded-C cases. A launch-time check would need a host-side wrapper, since N is a runtime argument of the @flyc.jit launcher; this PR does not add one. The title and description are updated to match.

jhinpan added a commit that referenced this pull request Oct 13, 2026
launch_gemm_a8w8 requires N % tile_n == 0, K % tile_k == 0 and
K // tile_k >= num_buffers - 1 (blockscale also N % 128 == 0); its
docstring now says so. For a padded C, _run_and_check fills the columns
past N with a signaling-NaN bit pattern and checks that they keep it, so
any store past N fails the test, a NaN store included.

Folded in from #1229.

Signed-off-by: Jin Pan <jin.pan@amd.com>
@jhinpan

jhinpan commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator Author

Folded into #1264 as 38fffe7: the shape-contract docstring and the check that C stores stay inside N now land together with the tile check. Closing this one.

@jhinpan jhinpan closed this Oct 13, 2026
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.

3 participants