You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Follow-up to #6376 (Relu2 Tier 1+2 MoE GEMM support), which was reverted via #6460/#6463 due to a regression in the amd/MiniMax-M3-MXFP4 TP4 production job: moe_dispatch: unsupported (activation=2, has_bias=0, split_k=1) combination.
Root cause
Two independent gaps combined to produce this failure:
Codegen gap (the real root cause).csrc/ck_tile_gemm_moe_2stages/gen_instances.py derived has_bias as a single value per (act_type, is_split_k) pair instead of sweeping it independently. For SwiGLU non-split (split_k<=1), this meant only the has_bias=True kernel instance was ever generated. The C++ dispatch branch in moe_dispatch() for (SwiGLU, no-bias, split_k<=1)already existed and was correct — it called moe_gemm2_heuristic_dispatcher<...,2,false,false>::dispatch() — but with no generated specialization behind it, the call fell through to the empty primary (unspecialized) template, which is undefined behavior (confirmed: a literal empty-bodied function with a non-void return type). This was silent UB before [HIP] [CK] Re-submit Relu2 Tier 2 MoE GEMM support (clean CK pin) #6376 added a fail-loud TORCH_CHECK guard — the guard didn't introduce the bug, it correctly surfaced a pre-existing one.
Caller/metadata mismatch.get_2stage_cfgs() hardcoded has_bias=True, stage2_has_bias=True for every CK-Tile SwiGLU MXFP4 branch, regardless of the caller's real has_stage1_bias/has_stage2_bias. vLLM passes bias1=None, bias2=None for MiniMax-M3 (a4w4, not use_mxfp4_w4a16), so even after closing the codegen gap this mismatch would have resurfaced the same failure class on a future shape.
Fix
gen_instances.py: the outer codegen loop now sweeps has_bias in [True, False] for SwiGLU in both split_k modes (previously derived a single value from is_split_k). Also fixed a related bug this exposed: gen_heuristic_dispatch()'s has_bias template substitution was computing an iteration-invariant value that would have mislabeled the new no-bias instances as bias instances — now derives it from the actual sampled kernel instance.
fused_moe.py: get_2stage_cfgs()'s two CK-Tile SwiGLU MXFP4 branches now report the caller's real has_stage1_bias/has_stage2_bias instead of a hardcoded True.
test_moe_2stage.py: added test_swiglu_mxfp4_nonsplit_nobias_dispatch(), which asserts correct CK-Tile routing (has_bias=False, stage2_has_bias=False, ksplit<=1) and runs a passing end-to-end dispatch (no TORCH_CHECK/crash).
Verification
Targeted test (test_swiglu_mxfp4_nonsplit_nobias_dispatch) passes: correct dispatch-family/metadata assertions, end-to-end kernel call completes without raising.
Broader sweep on the real MiniMax-M3 shape (model_dim=6144, inter_dim=1536, E=129, topk=5) across token counts {1, 3, 5, 32, 128} x bias {on, off}: all 10 cases complete with no crashes; accuracy (logits_diff) is nearly identical between bias-on and bias-off at every token count, confirming the fix removes bias as a point of failure without changing kernel numerics.
Note: this specific CK-Tile SwiGLU-MXFP4 shape has an independent, pre-existing logits_diff≈0.07-0.075 accuracy characteristic in both the old (bias) and new (no-bias) configurations — reproduced identically either way, so it predates and is unrelated to this fix. Worth a separate investigation; not a blocker here.
Checklist (per revert-PR follow-up requirements)
Cover the required non-split/no-bias SwiGLU dispatch path
Remove the empty primary-dispatcher undefined behavior
Add a targeted test for (SwiGLU, has_bias=false, split_k=1)
Pass MiniMax-M3-MXFP4 TP4/TP8 validation through ci:vllm (existing coverage, label applied)
Add unaligned lengths covering partial vectors and scalar remainders
op_tests/test_activation.py:661
Every nonempty length here selects vec_size=1 on wave32 and wave64. These cases check the missing second load, but never exercise a partial vector or either scalar remainder loop in activation_kernel_vec_safe. Add unaligned lengths above the vectorization thresholds. On wave64, 129/131, 257/261, and 513/521 cover partial first and second vectors at widths 2, 4, and 8.
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Unresolved layout-routing regressions select incompatible weight interpretations, and the expanded test sweep includes an unsupported Relu2 combination.
Preserve dtype fallback for untagged weight layouts
aiter/fused_moe.py:4135
Preserve the bf16/fp16 fallback when layout metadata is absent. The generic shuffle_weight(..., layout=(16, 16)) path sets is_shuffled but not gate_up_interleave. An untuned, separated SwiGLU call with bf16 activations and GGUU weights therefore loses the standard-layout split-K path. The later CK-Tile fallback uses post_activation_layout="auto", which treats PyTorch FP4 weights as interleaved: it pairs the wrong gate/up groups, or raises if stage1 bias is present. Keep the previous dtype-based inference for untagged weights while honoring explicit layout metadata.
…ave gate_up_interleave unset (MiniMax-M3 TP8 regression from #6478) (#6620)
* [MoE] Keep the CK-Tile split-k route for SwiGLU MXFP4 callers that leave gate_up_interleave unset
#6478 made the CK-Tile split-k route (swiglu_mxfp4_bf16_cktile) require
w1.gate_up_interleave is False. Callers that never set the attribute pass None,
and with bf16/fp16 activations they now fall through to the "unshuffled weights"
CK-Tile fallback, which runs with post_activation_layout="auto" instead of
"standard". ATOM's MiniMax-M3-MXFP4 at TP8 (per-rank inter_dim 384 padded to 512)
hits exactly this on every decode step: the MoE output is uncorrelated with the
reference and generation runs to max tokens.
Treat an untagged layout (None) with bf16/fp16 activations as before #6478.
Tagged callers (True/False) are unchanged. Add a dispatch regression test for the
untagged case on the MiniMax-M3 TP8 shape.
* Use the MiniMax-M3 TP8 MoE call shape ATOM actually issues in the untagged-layout test
A server-side trace of get_2stage_cfgs shows inter_dim=512 with intermediate_pad=0
for this call, not intermediate_pad=128.
Since #6478 hands a caller's stage-1 bias to the CK-Tile split-k route,
MXFP4 callers with gate/up-interleaved weights and a bias reach its
interleaved epilogue, which cannot apply one. That is a missing feature,
not a bad argument, so raise NotImplementedError like fused_moe's other
unsupported combinations; the MoE replay records such caller-side
variants as unsupported rather than failing on them.
Signed-off-by: zhimding <zhimding@amd.com>
…curacy test
The test runs rocm_aiter_fused_experts with SWIGLUOAI and no bias but
scored it against a silu(gate) * up reference. It only passed because
AITER <= v0.1.25 had no CK-Tile instance for SwiGLU without bias and
dispatched to a SiLU kernel instead. AITER v0.1.26 (ROCm/aiter#6478)
adds the missing instance, so the kernel now computes gpt-oss SwiGLU and
the silu reference no longer matches.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: josh schell <josh.schell@amd.com>
…curacy test
The test runs rocm_aiter_fused_experts with SWIGLUOAI and no bias but
scored it against a silu(gate) * up reference. It only passed because
AITER <= v0.1.25 had no CK-Tile instance for SwiGLU without bias and
dispatched to a SiLU kernel instead. AITER v0.1.26 (ROCm/aiter#6478)
adds the missing instance, so the kernel now computes gpt-oss SwiGLU and
the silu reference no longer matches.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: josh schell <josh.schell@amd.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
[MoE] Fix missing SwiGLU non-split no-bias CK-Tile MXFP4 dispatch (follow-up to #6376 revert)
Summary
Follow-up to #6376 (Relu2 Tier 1+2 MoE GEMM support), which was reverted via #6460/#6463 due to a regression in the
amd/MiniMax-M3-MXFP4TP4 production job:moe_dispatch: unsupported (activation=2, has_bias=0, split_k=1) combination.Root cause
Two independent gaps combined to produce this failure:
Codegen gap (the real root cause).
csrc/ck_tile_gemm_moe_2stages/gen_instances.pyderivedhas_biasas a single value per(act_type, is_split_k)pair instead of sweeping it independently. For SwiGLU non-split (split_k<=1), this meant only thehas_bias=Truekernel instance was ever generated. The C++ dispatch branch inmoe_dispatch()for(SwiGLU, no-bias, split_k<=1)already existed and was correct — it calledmoe_gemm2_heuristic_dispatcher<...,2,false,false>::dispatch()— but with no generated specialization behind it, the call fell through to the empty primary (unspecialized) template, which is undefined behavior (confirmed: a literal empty-bodied function with a non-voidreturn type). This was silent UB before [HIP] [CK] Re-submit Relu2 Tier 2 MoE GEMM support (clean CK pin) #6376 added a fail-loudTORCH_CHECKguard — the guard didn't introduce the bug, it correctly surfaced a pre-existing one.Caller/metadata mismatch.
get_2stage_cfgs()hardcodedhas_bias=True, stage2_has_bias=Truefor every CK-Tile SwiGLU MXFP4 branch, regardless of the caller's realhas_stage1_bias/has_stage2_bias. vLLM passesbias1=None, bias2=Nonefor MiniMax-M3 (a4w4, notuse_mxfp4_w4a16), so even after closing the codegen gap this mismatch would have resurfaced the same failure class on a future shape.Fix
gen_instances.py: the outer codegen loop now sweepshas_biasin[True, False]for SwiGLU in both split_k modes (previously derived a single value fromis_split_k). Also fixed a related bug this exposed:gen_heuristic_dispatch()'shas_biastemplate substitution was computing an iteration-invariant value that would have mislabeled the new no-bias instances as bias instances — now derives it from the actual sampled kernel instance.fused_moe.py:get_2stage_cfgs()'s two CK-Tile SwiGLU MXFP4 branches now report the caller's realhas_stage1_bias/has_stage2_biasinstead of a hardcodedTrue.test_moe_2stage.py: addedtest_swiglu_mxfp4_nonsplit_nobias_dispatch(), which asserts correct CK-Tile routing (has_bias=False,stage2_has_bias=False,ksplit<=1) and runs a passing end-to-end dispatch (noTORCH_CHECK/crash).Verification
test_swiglu_mxfp4_nonsplit_nobias_dispatch) passes: correct dispatch-family/metadata assertions, end-to-end kernel call completes without raising.model_dim=6144, inter_dim=1536, E=129, topk=5) across token counts{1, 3, 5, 32, 128}x bias{on, off}: all 10 cases complete with no crashes; accuracy (logits_diff) is nearly identical between bias-on and bias-off at every token count, confirming the fix removes bias as a point of failure without changing kernel numerics.logits_diff≈0.07-0.075accuracy characteristic in both the old (bias) and new (no-bias) configurations — reproduced identically either way, so it predates and is unrelated to this fix. Worth a separate investigation; not a blocker here.Checklist (per revert-PR follow-up requirements)
(SwiGLU, has_bias=false, split_k=1)ci:vllm(existing coverage, label applied)