Skip to content

Remove the unreachable torch<2 attention path in the Kolors text encoder - #14620

Open
RudraMantri123 wants to merge 2 commits into
huggingface:mainfrom
RudraMantri123:fix-kolors-mps-baddbmm
Open

Remove the unreachable torch<2 attention path in the Kolors text encoder#14620
RudraMantri123 wants to merge 2 commits into
huggingface:mainfrom
RudraMantri123:fix-kolors-mps-baddbmm

Conversation

@RudraMantri123

@RudraMantri123 RudraMantri123 commented Aug 27, 2026

Copy link
Copy Markdown

Fixes #14624.

What does this PR do?

Removes CoreAttention's manual attention branch in pipelines/kolors/text_encoder.py — dead code on every supported torch, and the last place in the repository still carrying the baddbmm(input=torch.empty(...), beta=0) idiom that MPS executes incorrectly (pytorch/pytorch#187521).

Correction from the original version of this PR

The first commit here added an MPS-specific bmm branch next to the baddbmm call, mirroring #14459. That was the wrong fix, and I want to be explicit about why, since I only saw it on re-verification:

CoreAttention.forward gates on int(torch.__version__.split(".")[0]) >= 2 and routes everything through scaled_dot_product_attention on torch ≥ 2. diffusers requires torch ≥ 2.6, so the manual branch — the one containing the unsafe baddbmm idiom — is unreachable on any supported install. This is also why I could never reproduce end-to-end corruption in this pipeline: the code never runs. The branch my first commit added was itself dead code, and its "dirty allocator" test exercised SDPA all along and could never fail.

So the honest change is the second commit: delete the whole torch < 2 path.

What is verified

  • The idiom is genuinely unsafe where reachable: at this code's exact score shapes — (16, 1024, 1024) and (16, 2048, 2048) fp16 — a freed NaN block returned by torch.empty survives baddbmm(beta=0) on MPS (torch 2.13.0, M-series). Repro in Kolors text encoder carries an unreachable torch<2 attention path built on the MPS-unsafe baddbmm(empty, beta=0) idiom #14624. That is what made the branch worth removing rather than leaving as a trap for anyone who copies it or re-enables it.
  • The deletion changes nothing observable: outputs are bit-identical to main (torch.equal, max diff 0.0) across cpu/mps × fp32/fp16 × both mask branches of forward.
  • make quality, check_copies, check_ai, and check_forward_call_docstrings pass.

Tests

tests/pipelines/kolors/test_kolors_text_encoder.py pins CPU-equivalence of the remaining SDPA path — causal (mask=None) and explicit-mask branches — and runs on every backend with no weights or Hub access (CoreAttention is built directly from ChatGLMConfig).

Notes for reviewers

AI disclosure per the contribution policy: AI-assisted debugging and drafting; all experiments were run and verified by me on real hardware.

  • Net effect: −105 lines of unreachable code, +docstring-level tests, zero behaviour change.
  • If you would rather keep the vendored file byte-aligned with upstream ChatGLM and close this instead, that is a reasonable call — the hazard is latent either way. But carrying a provably-unsafe idiom in dead code seems strictly worse than carrying neither.

Who can review?

@yiyixuxu @asomoza

CoreAttention computes raw attention scores with
baddbmm(input=torch.empty(...), beta=0), relying on beta=0 causing input
to be ignored. MPS does not honour that contract (pytorch#187521): NaN in
the uninitialised buffer propagates into the output. Verified directly at
the shapes this code requests -- a freed NaN block of (b*np, sq, sk) is
handed back by torch.empty and survives baddbmm.

This is the last remaining instance of the idiom in the repository after
huggingface#14459 fixed both copies of Attention.get_attention_scores. Use the same
buffer-free scaled bmm on MPS, which relies on no contract and skips the
scores-sized allocation; all other devices keep the existing baddbmm path
unchanged.

Adds a CPU-equivalence test that runs on every backend and an MPS-gated
test that dirties the allocator before computing scores.
@github-actions

Copy link
Copy Markdown
Contributor

Hi @RudraMantri123, thanks for the PR! It does not appear to link an issue it fixes. If this PR addresses an existing issue, please add a closing keyword (e.g. Fixes #1234) to the PR description so the issue is linked. See the contribution guide for more details. If this PR intentionally does not fix a tracked issue, a maintainer can add the no-issue-needed label to silence this reminder.

Please note that PRs without a linked issue are likely to be automatically closed 10 days after this notice.

Once the PR links an issue (or gets the no-issue-needed label), you can ignore this message — it stays here as a comment, but it no longer applies.

@RudraMantri123

Copy link
Copy Markdown
Author

Linked: this now closes #14624, which documents the underlying hazard (unsafe baddbmm(empty, beta=0) on MPS at the score shapes this encoder requests, with the primitive-level repro and the honest scope caveat that end-to-end corruption was not demonstrated in this pipeline).

CoreAttention.forward gates on int(torch.__version__.split(".")[0]) >= 2 and
diffusers requires torch >= 2.6, so the manual attention branch -- the one
carrying the MPS-unsafe baddbmm(torch.empty(...), beta=0) idiom -- cannot
execute on any supported install. Every forward goes through
scaled_dot_product_attention.

That makes the previous commit's approach wrong twice over: the bug it
guarded against is unreachable, and the bmm branch it added was itself dead
code that nothing could execute. Delete the whole torch<2 path instead,
which removes the unsafe idiom truthfully.

Verified the deletion changes nothing observable: outputs are bit-identical
to main across cpu/mps, fp32/fp16, and both mask branches. The test file now
pins CPU-equivalence of the remaining SDPA path (masked and causal) on every
backend; the dirty-allocator test is dropped because it exercised SDPA all
along and could never fail.
@RudraMantri123 RudraMantri123 changed the title Avoid the unsafe baddbmm beta=0 idiom on MPS in the Kolors text encoder Remove the unreachable torch<2 attention path in the Kolors text encoder Aug 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Kolors text encoder carries an unreachable torch<2 attention path built on the MPS-unsafe baddbmm(empty, beta=0) idiom

1 participant