[Bugfix] Fix two SM80/SM120 forward kernel bugs: missing is_split_kv default, mDynamicCausal NameError - #156
Open
tgmerritt wants to merge 1 commit into
Conversation
…default, mDynamicCausal NameError
Found while enabling FA4 for SM120/SM121 (consumer Blackwell -- RTX PRO 6000,
GB10/DGX Spark) in vLLM v0.24.0. Both bugs are unconditional and architecture-
agnostic -- they'd break literal SM80 (Ampere) hardware too, since
FlashAttentionForwardSm120 subclasses FlashAttentionForwardSm80 and shares
this exact code. Confirmed both still present on main as of this PR.
## Bug 1: self.is_split_kv never set on Sm80/Sm120
FlashAttentionForwardBase.__init__ doesn't accept or set is_split_kv -- only
the Sm90/Sm100 family of constructors do (they receive it as an explicit
kwarg from interface.py's dispatch). But the shared __call__/kernel() code in
this file (lines ~364-412) reference self.is_split_kv unconditionally,
raising AttributeError on any Sm80 or Sm120 instantiation.
interface.py's SM120 dispatch branch (arch // 10 == 12) already asserts
`not is_split_kv` before constructing FlashAttentionForwardSm120 (split_kv
isn't supported on SM 12.0), so defaulting self.is_split_kv = False in
Sm80.__init__ is safe and doesn't change behavior for any caller that was
working before.
## Bug 2: mDynamicCausal NameError
__call__'s signature accepts an mDynamicCausal parameter, but never forwards
it into the paired @cute.kernel-decorated kernel() function's own parameter
list, nor into the .launch() call's args. kernel()'s body references the bare
name anyway:
psc = mDynamicCausal[batch_size] if const_expr(mDynamicCausal is not None) else None
Since kernel() is traced in isolation by the CuTe-DSL JIT compiler (it doesn't
have access to __call__'s local scope), this raises:
NameError: name 'mDynamicCausal' is not defined
This happens on EVERY call through this kernel, regardless of causal=True or
causal=False -- verified directly, both crash identically.
A complete fix would thread mDynamicCausal through kernel()'s parameter list
and the .launch() call. This patch instead defaults it to None right before
use, which only restores prior (pre-dynamic-causal) behavior for callers not
using that feature -- i.e. it unblocks the overwhelmingly common case (static
causal/non-causal) without attempting the larger plumbing change.
## Verification
Verified end-to-end on real GB10 (SM121) hardware via vLLM's actual production
call path (vllm.vllm_flash_attn.cute.interface.flash_attn_varlen_func), bf16,
head_dim=128, two varlen sequences (lengths 64/96), causal=True. Output
matches torch.nn.functional.scaled_dot_product_attention reference: max abs
diff 0.0078, mean abs diff 0.00025 (normal bf16 rounding). Without these two
fixes, the same call crashes with the NameError above; with only the
mDynamicCausal fix and not the is_split_kv fix, it crashes with AttributeError
instead.
Note: tested via vLLM's vendored copy of this file (which renames
`flash_attn.cute` imports to `vllm.vllm_flash_attn.cute` for its package
namespace) since that's the integration path I was working in; this diff is
against this repo's actual files and the same two line-level changes, not
re-run against a from-scratch build of this exact tree (that would require a
fresh ~70min CUDA compile). The fix is identical logic in both cases.
AI assistance disclosure: found and fixed with Claude Code (Anthropic) while
building a custom vLLM v0.24.0 image for GB10. I reviewed and understand the
change.
Signed-off-by: Tyler Merritt <tylerm@uneeq.com>
36 tasks
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Found while enabling FA4 for SM120/SM121 (consumer Blackwell — RTX PRO 6000, GB10/DGX Spark) in vLLM v0.24.0. Both bugs are unconditional and architecture-agnostic — they'd break literal SM80 (Ampere) hardware too, since
FlashAttentionForwardSm120subclassesFlashAttentionForwardSm80and shares this exact code path. Confirmed both still present onmain.Bug 1:
self.is_split_kvnever set on Sm80/Sm120FlashAttentionForwardBase.__init__doesn't accept or setis_split_kv— only the Sm90/Sm100 family of constructors do (they receive it explicitly frominterface.py's dispatch). But the shared__call__/kernel()code in this file referencesself.is_split_kvunconditionally, raisingAttributeErroron any Sm80 or Sm120 instantiation.interface.py's SM120 dispatch branch already assertsnot is_split_kvbefore constructingFlashAttentionForwardSm120(split_kv isn't supported on SM 12.0), so defaultingself.is_split_kv = FalseinSm80.__init__is safe.Bug 2:
mDynamicCausalNameError__call__'s signature acceptsmDynamicCausal, but never forwards it into the paired@cute.kernel-decoratedkernel()function's own parameter list or the.launch()call's args.kernel()'s body references the bare name anyway:Since
kernel()is traced in isolation by the CuTe-DSL JIT compiler, this raisesNameError: name 'mDynamicCausal' is not definedon every call, regardless ofcausal=True/False— verified both crash identically.A complete fix would thread
mDynamicCausalthroughkernel()'s parameter list and the.launch()call. This patch instead defaults it toNoneright before use, restoring prior (pre-dynamic-causal) behavior for callers not using that feature — i.e. unblocks the overwhelmingly common case (static causal/non-causal) without the larger plumbing change.Not fixed by #154 "Fix dynamic_causal" (merged) — that PR fixes a different
dynamic_causalbug inmask.py's masking logic, not this one.Verification
Verified end-to-end on real GB10 (SM121) hardware via vLLM's actual production call path (
flash_attn_varlen_func), bf16, head_dim=128, two varlen sequences (lengths 64/96), causal=True. Output matchestorch.nn.functional.scaled_dot_product_attentionreference: max abs diff 0.0078, mean abs diff 0.00025 (normal bf16 rounding). Without these two fixes the same call crashes (NameError, then AttributeError if only one is fixed).Note: tested via vLLM's vendored copy of this file (renames
flash_attn.cuteimports tovllm.vllm_flash_attn.cutefor its package namespace) since that's the integration path I was working in — same two line-level changes, not re-run against a from-scratch build of this exact tree (would require a fresh ~70min CUDA compile). The fix is identical logic in both cases.AI assistance disclosure
Found and fixed with Claude Code (Anthropic) while building a custom vLLM v0.24.0 image for GB10. I reviewed and understand the change.