Repository navigation
Conversation
There was a problem hiding this comment.
🟡 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)); |
| // 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 Report❌ Patch coverage is
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
🚀 New features to boost your workflow:
|
| 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>
| if(ins->name() != "concat" or ins->get_shape().dynamic()) | ||
| return false; | ||
| const auto& inputs = ins->inputs(); | ||
| if(inputs.size() != 2) |
There was a problem hiding this comment.
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


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 standaloneconcat_kernelfor every layer (32 launches per token):fuse_attentioncaptures the pointwise rotary math on Q into the rocMLIR attention prologue, but it stops atconcat, so the half-swap stayed a copy kernel in front of the fused attention. On the K side the rotary is a pointwise kernel thatfuse_concat_past_presentretargets 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 aswhere(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_opthen sinks the reshape past the rotarymul/add, so the attention prologue becomesslice → where → mul → add → reshape → transpose → dotand rocMLIR compiles it with no concat kernel. The K rotary becomes a singlewhere_mul_mul_addpointwise.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 andconcat_past_present(reshape_lazy, squeeze, unsqueeze, transpose) are inverted withshape_transform_descriptor::invertand applied to the cache slot view, which becomes the producer's output buffer and reportedoutput_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 isinvert_view_chain, now exported fromeliminate_concat.hppand shared witheliminate_concat.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_chainreshape, transpose+reshape, non-lazy and broadcast cases.Changelog Category
Add a
CHANGELOG.mdentry for any option other thanNot ApplicableFollow the LLVM AI Tool Use Policy for contributions using AI.