Conversation
pfultz2
marked this pull request as ready for review
September 16, 2026 15:28
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
The new rewrites can fail on dynamic or input-driven slices, collide on generated module names, and mishandle tuple-valued pointwise modules.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Extends GPU fusion to handle sliced/squeezed reductions, short-reduction tiling, and attention output view chains.
Changes:
- Adds reduction slice and squeeze-pointwise fusion rewrites.
- Adds short-reduction tiling candidates.
- Broadens attention matching and adds regression coverage.
Review used a single-pass analysis without agent fan-out.
File summaries
| File | Description |
|---|---|
src/fuse_reduce.cpp |
Adds slice and squeeze fusion rewrites. |
src/simplify_reshapes.cpp |
Supports unary fused-pointwise reshaping. |
src/fuse_attention.cpp |
Follows attention outputs through view operations. |
src/targets/gpu/jit/reduce.cpp |
Adds short-reduction tiling candidates. |
test/fuse_reduce.cpp |
Tests reduction fusion rewrites. |
test/simplify_reshapes_test.cpp |
Tests fused-pointwise reshaping. |
test/fuse_attention.cpp |
Tests unsqueezed attention outputs. |
test/verify/test_unpack_int4_dequant_reduce_slice_swiglu.cpp |
Verifies sliced int4 SwiGLU reductions. |
test/verify/test_unpack_int4_dequant_reduce_tiled.cpp |
Verifies tiled int4 reductions. |
Review details
- Files reviewed: 9/9 changed files
- Comments generated: 5
- Review effort level: Balanced
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+572
to
+573
| return match::name("slice")( | ||
| match::arg(0)(match::skip(unit_reshapes)(match::name("fused_reduce").bind("reduce")))); |
| if(input != reduce and reduce->outputs().size() != 1) | ||
| return; | ||
| std::reverse(ops.begin(), ops.end()); | ||
| const auto& rlens = reduce->get_shape().lens(); |
| // Broadcasts inside the submodule expand to the full axis | ||
| auto new_len = static_cast<std::size_t>(end - start); | ||
| const auto* oldm = reduce->module_inputs().front(); | ||
| auto* sm = mpm.create_module(oldm->name() + "_slice" + std::to_string(start)); |
Comment on lines
+1758
to
+1760
| // Fused pointwise modules are unary when they have a single input | ||
| auto unary = match::any_of(match::pointwise(), match::name("pointwise")); | ||
| return unary( |
Comment on lines
+630
to
+633
| const auto& device = ctx.get_current_device(); | ||
| auto resident = device.get_cu_count() * device.get_max_workitems_per_cu() / block_size; | ||
| if(reduce_output_shape.elements() < resident) | ||
| return nullopt; |
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.
Motivation
In int4 decode the matvecs are already fused reductions (dequantize, mul, reduce_sum), but two common patterns around them still fall out of the fusion:
Short reductions with many outputs (blocked int4 weights with a small K) also leave each lane of a full workgroup with only a few elements, so per-workgroup overhead dominates and too few loads are in flight.
Separately, once the projection dot after attention is rewritten to a reduction,
simplify_reshapesreshuffles the transpose/reshape epilogue after the second gemm (for example leaving an unsqueeze in it).fuse_attentionanchored on the exactreshape(transpose(dot))shape, so it stopped matching and the attention was left unfused.Technical Details
fuse_reduce
find_reduce_slice: when every consumer of afused_reduceis a slice along the same non-reduced axis, the reduction is split into one reduce per slice. The slices are pushed onto the reduce inputs, the submodule is re-fused with its broadcasts narrowed to the sliced length, and any unit reshapes between the reduce and the slice are replayed on the new reduce. The slice consumers can then fuse with their own reduction (SwiGLU over the gate_up halves). It waits until anyunpack_int4feeding the reduce has been fused, since the slices move into the reduce inputs.find_reduce_squeeze_pointwise: a pointwise over a squeezedfused_reduceoutput is moved into the reduce space: the other inputs are unsqueezed instead and the squeeze is applied after the pointwise, so the existing reduce+pointwise fusion picks it up as an epilogue.simplify_reshapes
find_unary_shape_transformsnow also treats a fusedpointwisemodule with a single input as unary, so a squeeze or reshape can move across it. The module inputs are preserved when the instruction is re-inserted.GPU fused reduce compiler
find_short_reduce_tile: when no broadcast-based tile is found, and the tiled block size for the reduction is below a full workgroup while the outputs still fill the device, the last non-reduced axis is tiled two outputs per workgroup. This amortizes the per-workgroup overhead over two reductions and doubles the loads in flight per lane. The tile is only offered to the tuner (block_tile / block_batch), which benchmarks it against the plain block algorithm.fuse_attention
find_kv_cache_attentionnow ends its match at the second gemm instead ofreshape(transpose(dot)).find_output_endwalks the single-consumer view ops after the gemm (transpose, reshape, unsqueeze, squeeze) to find where the attention output ends, so the fusion works in whatever form the reshape simplifications left the epilogue.Tests
fuse_reduce:reduce_slice_pointwise,reduce_squeeze_pointwisesimplify_reshapes:pointwise_module_reshape_unaryfuse_attention:kv_cache_attention_unsqueezed_outputtest_unpack_int4_dequant_reduce_slice_swiglu,test_unpack_int4_dequant_reduce_tiledChangelog Category
Add a
CHANGELOG.mdentry for any option other thanNot ApplicableFollow the LLVM AI Tool Use Policy for contributions using AI.