[Perf] Keep runtime base and static tail separate in add_offset [wip] - #933
Open
Phil-amd wants to merge 1 commit into
Open
[Perf] Keep runtime base and static tail separate in add_offset [wip]#933Phil-amd wants to merge 1 commit into
Phil-amd wants to merge 1 commit into
Conversation
The nested-add_offset rule fused unconditionally, collapsing add_offset(add_offset(smem, sw), const) into add_offset(smem, sw + const), so every LDS read computed its own address and needed its own base VGPR. AddOffsetCanonicalization converges a chain to add_offset(add_offset(ptr, dyn), static) instead. static+static and dynamic+dynamic still fuse; only the swap needs a single-use inner op. gfx950 SWA attention: 38 -> 3 ds_read base registers, 44 spills eliminated, ~26% faster -- matching recast_iter without needing it. A non-trivial swizzle sees no benefit: the XOR at the load consumes the static tail. Fixes #898.
Phil-amd
force-pushed
the
phil/add-offset-canonicalization
branch
from
July 31, 2026 10:29
2462a56 to
5aefa2b
Compare
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.
Summary
The nested-
add_offsetrewrite inMemrefLowering.tdfused unconditionally, collapsingadd_offset(add_offset(smem, sw), const)intoadd_offset(smem, sw + const). Withsmema relocatable LDS symbol, every read then computed its own address off the runtime base and needed its own base VGPR. This replaces the rule with a canonicalization that keeps the runtime base and the static tail separate.Motivation
Reported in #898. On gfx950 the SWA attention kernel showed 38 distinct
ds_read_b64_tr_b16base registers, 44 VGPR spills and 44/27 scratch traffic. Therecast_iterform — which the old pattern did not match, so it acted as an accidental fusion barrier — showed 3 bases and no spills, and ran ~26% faster. Kernels have been working around this by insertingrecast_iter; this removes the need.One correction to the issue's diagnosis, from the measured ISA: constants do fold into the
ds_read offset:immediate in both forms (252/256 either way). The defect is base-register scatter, not failed immediate folding. The relevant metric is the distinct base-register count.Changes
MemrefLowering.td.AddOffsetCanonicalizationinLayoutLowering.cpp, converging anadd_offsetchain toadd_offset(add_offset(ptr, dyn), static)— at most one dynamic layer plus one static tail.static+staticanddynamic+dynamicstill fuse; neither merges a runtime value with a constant.fused_add_rmsnorm_kernel_0(14add_offsetops became 115, 6 LLVMadds became 102).dyn -> staticform is rejected, so the pattern cannot ping-pong. Three-level chains are covered by tests.Performance
gfx950, SWA attention repro from the issue,
use_recast=False(the plain form):The plain form now matches the
recast_iterform exactly, with the two within measurement noise of each other.The benefit is bimodal rather than proportional: it comes from crossing the spill threshold, not from the handful of saved VALU adds. Kernels that were not close to spilling see no change — a per-kernel resource comparison against this base shows 32 kernels all unchanged on gfx942, no regressions and no improvements.
Applicability boundary: a non-trivial swizzle is applied to the final address at the load, so the static tail is consumed by the XOR and cannot become an immediate. The canonicalization is neutral there, not harmful. This is asserted by a test so the boundary stays visible.
Testing
layout_lowering.mlir(four static/dynamic combinations, three-level chains, multi-use inner, shared runtime base, zero/negative constants) and 3 conversion cases innested_add_offset.mlir(LDS, buffer, swizzle). Both files were verified to fail with the source change reverted.clang-formatclean.Breaking Changes
None. Kernels using
recast_iteras a workaround keep working unchanged —recast_iterlowers to a no-op — they simply no longer need it for this.