Skip to content

Fuse the rotate-half concat into attention and let the K rotary write the KV cache directly - #5379

Open
pfultz2 wants to merge 4 commits into
developfrom
concat-swapped-halves
Open

pfultz2 wants to merge 4 commits into
developfrom
concat-swapped-halves

Conversation

@pfultz2

@pfultz2 pfultz2 commented Oct 12, 2026

Copy link
Copy Markdown
Collaborator

Motivation

In RoPE models the rotary embedding needs rotate_half(x) = concat(x[h:], x[:h]). On Phi-3.5 decode this concat ran as a standalone concat_kernel for every layer (32 launches per token): fuse_attention captures the pointwise rotary math on Q into the rocMLIR attention prologue, but it stops at concat, so the half-swap stayed a copy kernel in front of the fused attention. On the K side the rotary is a pointwise kernel that fuse_concat_past_present retargets to write straight into the cache, but only when the producer's shape was identical to the concat input, which excluded both the reshaped rotary producer and the seq-major prefill layout.

Removing those copies is pure launch-count savings on a launch-bound decode path.

Technical Details

  • simplify_algebra: find_concat_swapped_halves. A concat of the two equal halves of one tensor in swapped order (identical slicing on every other axis) is rewritten as where(mask, hi, lo) over the concat axis split into {2, half}, with the halves as unsqueezed broadcast views and a {true, false} mask literal, followed by a reshape. find_op_shape_transform_op then sinks the reshape past the rotary mul/add, so the attention prologue becomes slice → where → mul → add → reshape → transpose → dot and rocMLIR compiles it with no concat kernel. The K rotary becomes a single where_mul_mul_add pointwise.
  • fuse_concat_past_present: invert the view chain. The producer no longer has to match the concat input shape. The view ops between the producer and concat_past_present (reshape_lazy, squeeze, unsqueeze, transpose) are inverted with shape_transform_descriptor::invert and applied to the cache slot view, which becomes the producer's output buffer and reported output_shape. This fuses the 5-D rotary producer at decode and the seq-major (non-standard) producer at prefill, so the K append copy disappears in both cases. The inversion helper is invert_view_chain, now exported from eliminate_concat.hpp and shared with eliminate_concat.
  • Tests. simplify_algebra: positive single-axis and multi-axis cases plus unequal halves, different sources and mismatched other-axis slices. fuse_concat_past_present: reshaped, transposed and prefill seq-major producers, and an unmergeable reshape that must not fuse. eliminate_concat: invert_view_chain reshape, transpose+reshape, non-lazy and broadcast cases.

Changelog Category

Add a CHANGELOG.md entry for any option other than Not Applicable

    • Added: New functionality.
    • Changed: Changes to existing functionality.
    • Removed: Functionality or support that has been removed. (Compared to a previous release)
    • Optimized: Component performance that has been optimized or improved.
    • Resolved Issues: Known issues from a previous version that have been resolved.
    • Not Applicable: This PR is not to be included in the changelog.

Follow the LLVM AI Tool Use Policy for contributions using AI.

@pfultz2
pfultz2 requested a review from causten as a code owner October 12, 2026 22:55
Copilot AI balanced review requested due to automatic review settings October 12, 2026 22:55

Copilot AI 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.

🟡 Changes recommended

The new cache-layout paths lack runtime verification, and the required optimization changelog entry is missing.

2 open findings
What changed in this PR

Optimizes RoPE attention by eliminating rotate-half concat copies and writing rotary K results directly into the KV cache.

Changes:

  • Rewrites swapped-half concats into fusible pointwise operations.
  • Inverts view chains for direct cache writes.
  • Adds structural and helper tests.

Review was single-pass without agent fan-out.

File Description
src/​simplify_algebra.cpp Adds swapped-half concat rewrite.
src/​eliminate_concat.cpp Extracts view-chain inversion logic.
src/​include/​migraphx/​eliminate_concat.hpp Exports the inversion helper.
src/​targets/​gpu/​fuse_concat_past_present.cpp Retargets viewed producers into cache slots.
test/​simplify_algebra_test.cpp Tests concat rewrite eligibility.
test/​eliminate_concat_test.cpp Tests view-chain inversion.
test/​gpu/​fuse_concat_past_present.cpp Tests reshaped and transposed producer fusion.

🧠 Review effort: Balanced


💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

chain.rend(),
std::back_inserter(chain_ops),
[](instruction_ref x) { return x->get_operator(); });
auto inverse = invert_view_chain(chain_ops, producer->get_shape(), slot_shape(cur, cache));
Comment thread src/simplify_algebra.cpp
// Replace a swapped-halves concat with a `where` over the split axis, so the
// data movement becomes a pointwise op over views that can fuse into its
// consumers (such as the rotary prologue of attention), with no copy kernel.
struct find_concat_swapped_halves
@codecov

codecov Bot commented Oct 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.71429% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/eliminate_concat.cpp 88.89% 3 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #5379      +/-   ##
===========================================
- Coverage    92.71%   92.71%   -0.01%     
===========================================
  Files          635      635              
  Lines        36203    36851     +648     
===========================================
+ Hits         33565    34163     +598     
- Misses        2638     2688      +50     
Files with missing lines Coverage Δ
src/include/migraphx/eliminate_concat.hpp 100.00% <ø> (ø)
src/simplify_algebra.cpp 97.68% <100.00%> (+0.07%) ⬆️
src/eliminate_concat.cpp 94.00% <88.89%> (+1.95%) ⬆️

... and 9 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@pfultz2
pfultz2 requested review from CharlieL7, bdevorem and kahmed10 and a balanced review from Copilot October 14, 2026 15:49

Copilot AI 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.

🟡 Changes recommended

Cache retargeting can create invalid instruction ordering and trigger shape-propagation failures.

4 open findings

🧠 Review effort: Balanced

Comment thread src/targets/gpu/fuse_concat_past_present.cpp Outdated
assert(view->get_shape() == inverse->second);
auto v = producer->get_operator().to_value();
v["output_shape"] = to_value(view->get_shape());
v["output_shape"] = to_value(inverse->second);
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Comment thread src/simplify_algebra.cpp
if(ins->name() != "concat" or ins->get_shape().dynamic())
return false;
const auto& inputs = ins->inputs();
if(inputs.size() != 2)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

shouldn't this be inputs.size() % 2 != 0 since we want to check two equal halves? Right now this assumes only two inputs. This should ensure every concat checked then has inputs that would be divisible evenly

This branch has not been deployed

No deployments
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