[ROCm][CI] Mirror split kernel groups on MI355 and fix exposed tests (#58654)
Signed-off-by: Andreas Karatzas <andreas.karatzas@protonmail.com> Signed-off-by: Codex <codex@openai.com> Signed-off-by: Andreas Karatzas <akaratza@amd.com> Co-authored-by: OpenAI Codex <noreply@openai.com>
Andreas Karatzas committed
Sep 29, 2026 at 02:08 UTC
69d5c81fcd52f29a3b498dd4c403b088c91e4ee1
6 files changed
+209
-68
.buildkite/test-amd.yaml
+74
-28
@@ -4487,6 +4487,77 @@ steps:
4487
4488
#---------------------------------------------------------- mi355 · kernels ----------------------------------------------------------#
4489
4490
+- label: ":amd: (MI355) mHC Kernels"
4491
+ timeout_in_minutes: 50
4492
+ mirror_hardwares: [amdexperimental, amdproduction, amdgfx950nightly, amdmi355]
4493
+ dind: false
4494
+ agent_pool: mi355_1
4495
+ optional: true
4496
+ working_dir: "/vllm-workspace/tests"
4497
+ source_file_dependencies:
4498
+ - csrc/
4499
+ - vllm/
4500
+ - tests/kernels/
4501
+ - vllm/platforms/rocm.py
4502
+ commands:
4503
+ - pytest -v -s kernels/mhc
4504
+
4505
+- label: ":amd: (MI355) FLA Ops Kernels"
4506
+ timeout_in_minutes: 50
4507
+ mirror_hardwares: [amdexperimental, amdproduction, amdgfx950nightly, amdmi355]
4508
+ dind: false
4509
+ agent_pool: mi355_1
4510
+ optional: true
4511
+ working_dir: "/vllm-workspace/tests"
4512
+ source_file_dependencies:
4513
+ - csrc/
4514
+ - vllm/
4515
+ - tests/kernels/
4516
+ - vllm/platforms/rocm.py
4517
+ commands:
4518
+ - pytest -v -s kernels/fla
4519
+
4520
+- label: ":amd: (MI355) Miscellaneous Kernels"
4521
+ timeout_in_minutes: 55
4522
+ mirror_hardwares: [amdexperimental, amdproduction, amdgfx950nightly, amdmi355]
4523
+ dind: false
4524
+ agent_pool: mi355_1
4525
+ optional: true
4526
+ working_dir: "/vllm-workspace/tests"
4527
+ source_file_dependencies:
4528
+ - csrc/
4529
+ - vllm/
4530
+ - tests/kernels/
4531
+ - vllm/platforms/rocm.py
4532
+ commands:
4533
+ - pytest -v -s
4534
+ kernels/test_apply_repetition_penalties.py
4535
+ kernels/test_awq_int4_to_int8.py
4536
+ kernels/test_bf16_skinny_gemm.py
4537
+ kernels/test_cache_kernels.py
4538
+ kernels/test_compressor_kv_cache.py
4539
+ kernels/test_cp_gather_fp8.py
4540
+ kernels/test_deepseek_v4_cpu_kernels.py
4541
+ kernels/test_dsv41_mega_attn_layouts.py
4542
+ kernels/test_engram.py
4543
+ kernels/test_flashinfer_bf16_gemm.py
4544
+ kernels/test_flex_attention.py
4545
+ kernels/test_fp32_router_gemm.py
4546
+ kernels/test_fused_deepseek_v32_norm_rope.py
4547
+ kernels/test_fused_indexer_q_rope_quant.py
4548
+ kernels/test_fused_inv_rope_fp8_quant.py
4549
+ kernels/test_fused_minimax_m3_qknorm_rope_kv_insert.py
4550
+ kernels/test_fused_quant_activation.py
4551
+ kernels/test_gate_linear_rocm_dispatch.py
4552
+ kernels/test_gemm_rs_ar.py
4553
+ kernels/test_kpool_decode_update_batched.py
4554
+ kernels/test_minimax_m3_amd_ops.py
4555
+ kernels/test_minimax_m3_sparse_attn_fp8_scale.py
4556
+ kernels/test_onednn.py
4557
+ kernels/test_relu2_fp8_quant.py
4558
+ kernels/test_rocm_fp32_router_gemm.py
4559
+ --deselect=tests/kernels/test_bf16_skinny_gemm.py::test_every_dsv3_routed_shape_is_instantiated
4560
+
4561
- label: ":amd: (MI355 DPX) Attention DiffKV Kernels"
4562
timeout_in_minutes: 35
4563
mirror_hardwares: [amdexperimental, amdproduction, amdgfx950nightly, amdmi355]
@@ -5191,36 +5262,9 @@ steps:
5262
- unset VLLM_USE_V2_MODEL_RUNNER
5263
- uv pip install --system torchao==0.17.0
5264
- uv pip install --system conch-triton-kernels
5194
- - VLLM_TEST_FORCE_LOAD_FORMAT=auto pytest -v -s quantization/ --ignore quantization/test_blackwell_moe.py --ignore quantization/test_rocm_moe.py --shard-id=$$BUILDKITE_PARALLEL_JOB --num-shards=$$BUILDKITE_PARALLEL_JOB_COUNT
5265
+ - VLLM_TEST_FORCE_LOAD_FORMAT=auto pytest -v -s quantization/ --ignore quantization/test_blackwell_moe.py --ignore quantization/test_rocm_moe.py --ignore quantization/test_gfx950_moe.py --shard-id=$$BUILDKITE_PARALLEL_JOB --num-shards=$$BUILDKITE_PARALLEL_JOB_COUNT
5266
- if [ "$$BUILDKITE_PARALLEL_JOB" = "0" ]; then pytest -v -s rocm/test_moe_weight_replay.py; fi
5267
5197
-# - label: Quantized MoE Test (B200-MI355) # TBD
5198
-# timeout_in_minutes: 180
5199
-# mirror_hardwares: [amdexperimental, amdproduction, amdgfx950nightly, amdmi355]
5200
-# dind: false
5201
-# agent_pool: mi355_1
5202
-# working_dir: "/vllm-workspace/"
5203
-# source_file_dependencies:
5204
-# - tests/quantization/test_gfx950_moe.py
5205
-# - vllm/model_executor/models/deepseek_v2.py
5206
-# - vllm/model_executor/models/gpt_oss.py
5207
-# - vllm/model_executor/models/llama4.py
5208
-# - vllm/model_executor/layers/fused_moe
5209
-# - vllm/model_executor/layers/quantization/compressed_tensors
5210
-# - vllm/model_executor/layers/quantization/modelopt.py
5211
-# - vllm/model_executor/layers/quantization/mxfp4.py
5212
-# - vllm/v1/attention/backends/triton_attn.py
5213
-# - vllm/v1/attention/backends/rocm_attn.py
5214
-# - vllm/v1/attention/backends/rocm_aiter_fa.py
5215
-# - vllm/v1/attention/backends/mla/
5216
-# - vllm/v1/attention/selector.py
5217
-# - vllm/model_executor/layers/layernorm.py
5218
-# - vllm/_aiter_ops.py
5219
-# - vllm/platforms/rocm.py
5220
-# - vllm/model_executor/model_loader/
5221
-# commands:
5222
-# - pytest -s -v tests/quantization/test_gfx950_moe.py
5223
-
5268
#--------------------------------------------------------- mi355 · samplers ----------------------------------------------------------#
5269
5270
- label: ":amd: (MI355 DPX) Samplers Multimodal Beam Search"
@@ -6078,6 +6122,7 @@ steps:
6122
timeout_in_minutes: 45
6123
working_dir: /vllm-workspace/
6124
source_file_dependencies:
6125
+ - tests/quantization/test_gfx950_moe.py
6126
- tests/quantization/test_rocm_moe.py
6127
- tests/utils.py
6128
- vllm/model_executor/models/deepseek_v2.py
@@ -6094,6 +6139,7 @@ steps:
6139
- vllm/envs.py
6140
commands:
6141
- pytest -v -s tests/quantization/test_rocm_moe.py
6142
+ - pytest -v -s tests/quantization/test_gfx950_moe.py
6143
dind: false
6144
agent_pool: mi355_1
6145
num_gpus: 1
.buildkite/test_areas/kernels.yaml
+101
-28
@@ -184,6 +184,20 @@ steps:
184
- tests/kernels/
185
commands:
186
- pytest -v -s kernels/mhc
187
+ mirror:
188
+ amd:
189
+ label: ":amd: (MI355) mHC Kernels"
190
+ dind: false
191
+ device: mi355_1
192
+ timeout_in_minutes: 50
193
+ working_dir: "/vllm-workspace/tests"
194
+ depends_on:
195
+ - image-build-amd
196
+ source_file_dependencies:
197
+ - csrc/
198
+ - vllm/
199
+ - tests/kernels/
200
+ - vllm/platforms/rocm.py
201
202
- label: ":nvidia: (B200) FLA Ops Kernels"
203
key: kernels-fla-ops-test-b200
@@ -196,6 +210,20 @@ steps:
210
- tests/kernels/
211
commands:
212
- pytest -v -s kernels/fla
213
+ mirror:
214
+ amd:
215
+ label: ":amd: (MI355) FLA Ops Kernels"
216
+ dind: false
217
+ device: mi355_1
218
+ timeout_in_minutes: 50
219
+ working_dir: "/vllm-workspace/tests"
220
+ depends_on:
221
+ - image-build-amd
222
+ source_file_dependencies:
223
+ - csrc/
224
+ - vllm/
225
+ - tests/kernels/
226
+ - vllm/platforms/rocm.py
227
228
# Catch-all for test files at the tests/kernels root. This job collects
229
# the whole root so new files are wired by default.
@@ -231,6 +259,50 @@ steps:
259
--ignore=kernels/test_shuffle_rows.py
260
# BROKEN on main, pending kernel fixes (B200):
261
# test_shuffle_rows.py (1: test_shuffle_rows_edge_cases)
262
+ mirror:
263
+ amd:
264
+ label: ":amd: (MI355) Miscellaneous Kernels"
265
+ dind: false
266
+ device: mi355_1
267
+ timeout_in_minutes: 55
268
+ working_dir: "/vllm-workspace/tests"
269
+ depends_on:
270
+ - image-build-amd
271
+ source_file_dependencies:
272
+ - csrc/
273
+ - vllm/
274
+ - tests/kernels/
275
+ - vllm/platforms/rocm.py
276
+ # Keep the ROCm exclusions from #50519: incompatible FlyDSL TurboQuant
277
+ # and a CUDA source check whose files are absent from the AMD test image.
278
+ commands:
279
+ - pytest -v -s
280
+ kernels/test_apply_repetition_penalties.py
281
+ kernels/test_awq_int4_to_int8.py
282
+ kernels/test_bf16_skinny_gemm.py
283
+ kernels/test_cache_kernels.py
284
+ kernels/test_compressor_kv_cache.py
285
+ kernels/test_cp_gather_fp8.py
286
+ kernels/test_deepseek_v4_cpu_kernels.py
287
+ kernels/test_dsv41_mega_attn_layouts.py
288
+ kernels/test_engram.py
289
+ kernels/test_flashinfer_bf16_gemm.py
290
+ kernels/test_flex_attention.py
291
+ kernels/test_fp32_router_gemm.py
292
+ kernels/test_fused_deepseek_v32_norm_rope.py
293
+ kernels/test_fused_indexer_q_rope_quant.py
294
+ kernels/test_fused_inv_rope_fp8_quant.py
295
+ kernels/test_fused_minimax_m3_qknorm_rope_kv_insert.py
296
+ kernels/test_fused_quant_activation.py
297
+ kernels/test_gate_linear_rocm_dispatch.py
298
+ kernels/test_gemm_rs_ar.py
299
+ kernels/test_kpool_decode_update_batched.py
300
+ kernels/test_minimax_m3_amd_ops.py
301
+ kernels/test_minimax_m3_sparse_attn_fp8_scale.py
302
+ kernels/test_onednn.py
303
+ kernels/test_relu2_fp8_quant.py
304
+ kernels/test_rocm_fp32_router_gemm.py
305
+ --deselect=tests/kernels/test_bf16_skinny_gemm.py::test_every_dsv3_routed_shape_is_instantiated
306
307
- label: ":nvidia: (L4) Attention Kernels Shard %N"
308
key: kernels-attention-test
@@ -725,6 +797,35 @@ steps:
797
- pytest -v -s kernels/moe/test_flashinfer_moe.py
798
- pytest -v -s kernels/moe/test_nvfp4_moe.py
799
- pytest -v -s kernels/moe/test_ocp_mx_moe.py
800
+ mirror:
801
+ amd:
802
+ label: ":amd: (MI355 DPX) FP4 and INT4 MoE Kernels"
803
+ dind: false
804
+ device: mi355_dpx
805
+ num_devices: 1
806
+ timeout_in_minutes: 40
807
+ optional: false
808
+ working_dir: /vllm-workspace/tests
809
+ depends_on:
810
+ - image-build-amd
811
+ source_file_dependencies:
812
+ - csrc/moe/
813
+ - csrc/rocm/
814
+ - vllm/model_executor/layers/fused_moe/
815
+ - vllm/model_executor/layers/quantization/
816
+ - vllm/_aiter_ops.py
817
+ - vllm/platforms/rocm.py
818
+ - vllm/envs.py
819
+ - vllm/config/
820
+ - tests/kernels/moe/test_rocm_aiter_moe.py
821
+ - tests/kernels/moe/test_ocp_mx_moe.py
822
+ - tests/kernels/moe/test_flydsl_moe.py
823
+ - tests/kernels/moe/utils.py
824
+ - tests/kernels/utils.py
825
+ commands:
826
+ - pytest -v -s kernels/moe/test_rocm_aiter_moe.py -k 'mi350_mxfp4 or quant_scheme_support_matches_gfx950'
827
+ - pytest -v -s kernels/moe/test_ocp_mx_moe.py::test_rocm_mxfp4_moe_oracle -k AITER_MXFP4
828
+ - pytest -v -s kernels/moe/test_flydsl_moe.py
829
830
831
- label: ":nvidia: (H100) FusedMoE Layer Kernels"
@@ -847,34 +948,6 @@ steps:
948
- pytest -v -s kernels/quantization/test_rocm_mxfp8_linear.py
949
- pytest -v -s kernels/quantization/test_rocm_skinny_gemms.py -k wvsplitkrc
950
850
-- label: ":amd: (MI355 DPX) FP4 and INT4 MoE Kernels"
851
- key: amd-fp4-moe-kernels-mi355
852
- dind: false
853
- device: mi355_dpx
854
- num_devices: 1
855
- timeout_in_minutes: 40
856
- working_dir: /vllm-workspace/tests
857
- depends_on:
858
- - image-build-amd
859
- source_file_dependencies:
860
- - csrc/moe/
861
- - csrc/rocm/
862
- - vllm/model_executor/layers/fused_moe/
863
- - vllm/model_executor/layers/quantization/
864
- - vllm/_aiter_ops.py
865
- - vllm/platforms/rocm.py
866
- - vllm/envs.py
867
- - vllm/config/
868
- - tests/kernels/moe/test_rocm_aiter_moe.py
869
- - tests/kernels/moe/test_ocp_mx_moe.py
870
- - tests/kernels/moe/test_flydsl_moe.py
871
- - tests/kernels/moe/utils.py
872
- - tests/kernels/utils.py
873
- commands:
874
- - pytest -v -s kernels/moe/test_rocm_aiter_moe.py -k 'mi350_mxfp4 or quant_scheme_support_matches_gfx950'
875
- - pytest -v -s kernels/moe/test_ocp_mx_moe.py::test_rocm_mxfp4_moe_oracle -k AITER_MXFP4
876
- - pytest -v -s kernels/moe/test_flydsl_moe.py
877
-
951
- label: ":amd: (MI355 DPX) FP8 MoE Kernels"
952
key: amd-fp8-moe-kernels-mi355
953
dind: false
.buildkite/test_areas/quantization.yaml
+2
@@ -102,6 +102,7 @@ steps:
102
depends_on:
103
- image-build-amd
104
source_file_dependencies:
105
+ - tests/quantization/test_gfx950_moe.py
106
- tests/quantization/test_rocm_moe.py
107
- tests/utils.py
108
- vllm/model_executor/models/deepseek_v2.py
@@ -118,6 +119,7 @@ steps:
119
- vllm/envs.py
120
commands:
121
- pytest -v -s tests/quantization/test_rocm_moe.py
122
+ - pytest -v -s tests/quantization/test_gfx950_moe.py
123
124
- label: ":nvidia: (H200 MIG 35GB) Quantized Models"
125
device: h200_35gb
tests/kernels/test_bf16_skinny_gemm.py
+3
@@ -269,6 +269,9 @@ def test_kda_overlap_configs_match_measured_table() -> None:
269
}
270
271
272
+@pytest.mark.skipif(
273
+ not current_platform.is_cuda(), reason="KDA CuTeDSL helper requires CUDA"
274
+)
275
@pytest.mark.parametrize(
276
"compute_capability,supported",
277
[((9, 0), False), ((10, 0), True), ((10, 3), True), ((12, 0), True)],
tests/kernels/test_compressor_kv_cache.py
+6
-2
@@ -2182,7 +2182,9 @@ def test_v41_rocm_csa2_full_pipeline():
2182
)
2183
2184
q_out, kv_out, index_q_out, index_q_scale_out, index_weights_out_out = result
2185
- assert torch.equal(q_out, q)
2185
+ assert torch.equal(
2186
+ q_out, q.view(num_tokens, attention.n_local_heads, attention.head_dim)
2187
+ )
2188
assert torch.equal(kv_out, kv)
2189
assert torch.equal(index_q_out, index_q)
2190
assert torch.equal(index_q_scale_out, index_q_scale)
@@ -2273,7 +2275,9 @@ def test_v41_rocm_csa2_reindex_pipeline():
2275
)
2276
2277
q_out, kv_out, index_q_out, index_q_scale_out, index_weights_out_out = result
2276
- assert torch.equal(q_out, q)
2278
+ assert torch.equal(
2279
+ q_out, q.view(num_tokens, attention.n_local_heads, attention.head_dim)
2280
+ )
2281
assert torch.equal(kv_out, kv)
2282
assert torch.equal(index_q_out, index_q)
2283
assert torch.equal(index_q_scale_out, index_q_scale)
tests/kernels/test_kpool_decode_update_batched.py
+23
-10
@@ -233,6 +233,26 @@ def _assert_eq(r_ref, r_kern):
233
)
234
235
236
+def _cache_pool_bytes(kv_cache: torch.Tensor, pool_slot: int) -> torch.Tensor:
237
+ """Read a logical pool's K and scale bytes from the platform cache layout."""
238
+ page_size = kv_cache.shape[1]
239
+ head_dim = kv_cache.shape[2] - 4
240
+ page_idx, token_offset = divmod(pool_slot, page_size)
241
+ flat = kv_cache[page_idx].reshape(-1)
242
+ dims = torch.arange(head_dim, device=kv_cache.device)
243
+ if current_platform.is_rocm() and page_size > 1:
244
+ k_offsets = (
245
+ (token_offset // 16) * 16 * head_dim
246
+ + (dims // 16) * 16 * 16
247
+ + (token_offset % 16) * 16
248
+ + dims % 16
249
+ )
250
+ else:
251
+ k_offsets = token_offset * head_dim + dims
252
+ scale_offset = page_size * head_dim + 4 * token_offset
253
+ return torch.cat((flat[k_offsets], flat[scale_offset : scale_offset + 4]))
254
+
255
+
256
@pytest.mark.skipif(not current_platform.is_rocm(), reason="ROCm required")
257
def test_amd_prefill_writer_uses_preshuffled_cache_layout():
258
from vllm.models.glm5next.amd.ops.kpool_compress import (
@@ -343,7 +363,7 @@ def test_decode_writer_matches_prefill_writer(pool_size, ring_pools):
363
p
364
for p in range(n_pools)
365
if not torch.equal(
346
- kv_prefill[p // page, p % page], kv_decode[p // page, p % page]
366
+ _cache_pool_bytes(kv_prefill, p), _cache_pool_bytes(kv_decode, p)
367
)
368
]
369
assert not differing, (
@@ -379,13 +399,6 @@ def test_rejected_draft_redo_needs_ring_slots(ring_pools):
399
kv = torch.zeros_like(kv_ref)
400
tail = torch.zeros(nblk, 2, ring, HEAD_DIM, dtype=torch.bfloat16, device=dev)
401
382
- def pool_bytes(cache, p):
383
- # Page layout: [page * HEAD_DIM bytes of K rows | page * 4 bytes of scales].
384
- flat = cache[0].reshape(-1)
385
- k_bytes = flat[p * HEAD_DIM : (p + 1) * HEAD_DIM]
386
- s_bytes = flat[page * HEAD_DIM + 4 * p : page * HEAD_DIM + 4 * (p + 1)]
387
- return torch.cat([k_bytes, s_bytes])
388
-
402
def step(positions, keys, scores):
403
pos = torch.tensor([positions], dtype=torch.int32, device=dev)
404
slots = [(p // pool) if p % pool == pool - 1 else -1 for p in positions]
@@ -415,7 +428,7 @@ def test_rejected_draft_redo_needs_ring_slots(ring_pools):
428
)
429
step([8, 9, 10, 11], k[8:12], score[8:12]) # all drafts rejected
430
for p in (1, 2):
418
- assert torch.equal(pool_bytes(kv, p), pool_bytes(kv_ref, p)), p
431
+ assert torch.equal(_cache_pool_bytes(kv, p), _cache_pool_bytes(kv_ref, p)), p
432
433
# Draft 7 completes pool 1 and is rejected. With a one-pool ring, drafts
434
# 8 and 9 overwrite the slots of positions 4 and 5, which are read by the
@@ -430,7 +443,7 @@ def test_rejected_draft_redo_needs_ring_slots(ring_pools):
443
torch.cat([score[6:7], draft_scores]),
444
)
445
step([7, 8, 9, 10], k[7:11], score[7:11])
433
- pool1_ok = torch.equal(pool_bytes(kv, 1), pool_bytes(kv_ref, 1))
446
+ pool1_ok = torch.equal(_cache_pool_bytes(kv, 1), _cache_pool_bytes(kv_ref, 1))
447
if ring_pools == 1:
448
assert not pool1_ok, "expected a one-pool ring to corrupt pool 1"
449
else: