Skip to content

feat(rocke): common lds allocation for a,b,cshuffle - #8844

Merged
bartekxk merged 5 commits into
developfrom
users/barkocot/rocke-common-lds
Jul 27, 2026
Merged

bartekxk merged 5 commits into
developfrom
users/barkocot/rocke-common-lds

Conversation

@bartekxk

@bartekxk bartekxk commented Jun 26, 2026 •

Copy link
Copy Markdown
Contributor

Motivation

Decrease the lds size used by gemm A+B and cshuffle.

Technical Details

  • change lds calc size formula
  • add lds common pool
  • add liveness analysis for lds

Test Plan

test_smem_liveness_packing.py

Test Result

passed

Submission Checklist

JIRA ID : AICK-1536
Changes are behind the feature flag HIPKERNELPROVIDER_ENABLE_ROCKE which is defaulted to OFF.

@bartekxk
bartekxk requested review from a team as code owners June 26, 2026 12:46
@bartekxk
bartekxk marked this pull request as draft June 26, 2026 12:46
@bartekxk bartekxk self-assigned this Jun 29, 2026
@bartekxk
bartekxk marked this pull request as ready for review June 29, 2026 13:41
@bartekxk bartekxk changed the title [rocKE] Common LDS allocation for A,B,CShuffle feat(rocke): common lds allocation for a,b,cshuffle Jun 30, 2026
Base automatically changed from users/barkocot/dsl-fixes-v3 to develop July 1, 2026 08:44
@therock-pr-bot

therock-pr-bot Bot commented Jul 1, 2026 •

Copy link
Copy Markdown

✅ All Checks Passed — Ready for Review

Check Status Details
🌿 Branch Name ✅ Pass —
📝 PR Title/Description ✅ Pass —
⛔ Forbidden Files ✅ Pass —
🧪 Unit Test ✅ Pass —
🔎 pre-commit ✅ Pass —
🚫 Draft PR 🔜 To Be Enabled —
🚩 Feature Flag 🔜 To Be Enabled —
📊 Code Coverage 🔜 To Be Enabled —
🤖 therock-pr-bot ✅ Pass —

🎉 All checks passed! This PR is ready for review.

📖 Need help? See the Policy FAQ for details on every check and how to fix failures.

@therock-pr-bot

therock-pr-bot Bot commented Jul 1, 2026 •

Copy link
Copy Markdown

🎉 All checks passed! This PR is ready for review.

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.

Pull request overview

This PR introduces an LDS (addrspace(3)) “smem pool” model for rocKE LLVM lowering, aiming to reduce total LDS usage by allowing non-overlapping allocations (e.g., GEMM A/B staging vs. cshuffle) to share the same underlying buffer based on liveness/interval packing.

Changes:

  • Replace per-allocation LDS globals with a single unified @smem_pool.<kernel> and per-allocation byte offsets in both Python and C++ lowerers.
  • Add live-interval analysis + greedy interval packing to compute LDS offsets and pool size.
  • Add unit tests for smem liveness + packing behavior; update golden IR hashes/sizes accordingly.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
dnn-providers/hip-kernel-provider/rocKE/Python/rocke/core/lower_llvm.py Implements smem liveness + interval packing and switches lowering to address via pooled LDS base pointers.
dnn-providers/hip-kernel-provider/rocKE/Cpp/core/lower_llvm/core.cpp C++ implementation of liveness + interval packing and pooled LDS base-pointer emission.
dnn-providers/hip-kernel-provider/rocKE/Cpp/core/lower_llvm/mem.cpp Updates smem-related lowerings to GEP from pooled LDS base pointer.
dnn-providers/hip-kernel-provider/rocKE/Cpp/core/lower_llvm/crosslane.cpp Updates transpose-read address computation to use pooled LDS base pointer.
dnn-providers/hip-kernel-provider/rocKE/Cpp/include/rocke/lower_llvm_internal.h Adds internal API/state for smem pool offsets and pool metadata.
dnn-providers/hip-kernel-provider/rocKE/Cpp/instances/common/conv_implicit_gemm_conv_spec_descriptors.cpp Updates LDS budget check to use max(A/B, C) consistent with pooled reuse; updates rejection message.
dnn-providers/hip-kernel-provider/rocKE/tests/core/test_smem_liveness_packing.py Adds new unit tests for liveness analysis and interval packing.
dnn-providers/hip-kernel-provider/rocKE/tests/golden/rocke_representative_ir_sha256.json Updates expected IR size/hash outputs after lowering changes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@bartekxk
bartekxk force-pushed the users/barkocot/rocke-common-lds branch from 4d73cb7 to dee280b Compare July 6, 2026 08:54
@bartekxk

bartekxk commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

The new implementation may emit an additional byte-level GEP to compute the allocation base. Can we cut that down?

aledudek added a commit that referenced this pull request Jul 22, 2026
Rebase of PR #8844 (users/barkocot/rocke-common-lds) onto the reorganized
develop (rocKE/ -> rocke/platform, Python/ -> platform/python, Cpp/ ->
platform/cpp).

Only the common-LDS-allocation half of the original PR is applied here: the
fp32/bf16 cshuffle half the branch also carried is already upstream on develop
(store ops, trunc_f32_to_bf16, dtype_d epilogues), so re-applying it was skipped
to avoid duplicate definitions.

Contents (net vs develop): a single liveness-packed addrspace(3) smem pool with
per-allocation byte offsets, DFS-preorder live-interval analysis + greedy
interval packing (non-interfering A/B and cshuffle-C share the pool: pool =
max(overlapping) instead of sum), and a per-(block,allocation) base-pointer
cache so a non-zero-offset segment emits one byte-GEP per block. Mirrored in
both the Python (core/lower_llvm.py) and C++ (cpp/core/lower_llvm/*) engines;
conv implicit-GEMM LDS budget switched to max(A/B, C); new
test_smem_liveness_packing.py; golden re-blessed for both llvm flavors.

Validation on the rebased tree: byte-identity GREEN 65/65 at llvm20 and llvm22;
pytest core (55) + test_rocke (231) + smem liveness (13) + golden gate (3) pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@aledudek
aledudek force-pushed the users/barkocot/rocke-common-lds branch from a4a1b71 to adb8467 Compare July 22, 2026 08:52
@therock-pr-bot

Copy link
Copy Markdown

Pre-commit check failed

⛔ pre-commit failed

Please run locally:

  • python -m pip install pre-commit
  • pre-commit install
  • pre-commit run --all-files --show-diff-on-failure

This repo uses .pre-commit-config.yaml.

@yraparti yraparti 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.

-Does this mandate coarse grained drain requirement between phases?
-While this is good to improve the throughput by maximizing the wave occupancy. Is there a hit on the latency by increasing the serialization.
-can we not enable this by default. Let the user/kernel developer use it on demand if they need more LDS memory (if above reasons are true).

@aledudek

Copy link
Copy Markdown
Contributor

-Does this mandate coarse grained drain requirement between phases? -While this is good to improve the throughput by maximizing the wave occupancy. Is there a hit on the latency by increasing the serialization. -can we not enable this by default. Let the user/kernel developer use it on demand if they need more LDS memory (if above reasons are true).

  1. No new drain/barrier. The pass only assigns byte offsets, it never inserts syncs. Allocations share LDS only when liveness proves their intervals don't overlap. For A/B - cshuffle-C, the K-loop's existing trailing block_sync_lds already separates the last A/B read from the first cshuffle write, so the reuse rides on a barrier that was already there.

  2. No latency hit. Nothing that overlapped before stops overlapping - the mainloop and cshuffle epilogue are already sequential (cshuffle needs the final accumulator). The only change is peak LDS: ab + c -> max(ab, c), which can raise occupancy. So it's a throughput win with no added serialization.

  3. Default-on is fine. Since there's no extra barrier and no serialization cost, the reuse only ever reduces LDS (worst case it's a no-op, falling back to sequential packing). It's also what lets the descriptor use max(ab, c) to admit specs that used to fail the LDS cap. Happy to add a flag to force sequential packing for debugging, but I'd keep it on by default. If you still think it's better to turn it off by default let me know.

aledudek added a commit that referenced this pull request Jul 27, 2026
Rebase of PR #8844 (users/barkocot/rocke-common-lds) onto the reorganized
develop (rocKE/ -> rocke/platform, Python/ -> platform/python, Cpp/ ->
platform/cpp).

Only the common-LDS-allocation half of the original PR is applied here: the
fp32/bf16 cshuffle half the branch also carried is already upstream on develop
(store ops, trunc_f32_to_bf16, dtype_d epilogues), so re-applying it was skipped
to avoid duplicate definitions.

Contents (net vs develop): a single liveness-packed addrspace(3) smem pool with
per-allocation byte offsets, DFS-preorder live-interval analysis + greedy
interval packing (non-interfering A/B and cshuffle-C share the pool: pool =
max(overlapping) instead of sum), and a per-(block,allocation) base-pointer
cache so a non-zero-offset segment emits one byte-GEP per block. Mirrored in
both the Python (core/lower_llvm.py) and C++ (cpp/core/lower_llvm/*) engines;
conv implicit-GEMM LDS budget switched to max(A/B, C); new
test_smem_liveness_packing.py; golden re-blessed for both llvm flavors.

Validation on the rebased tree: byte-identity GREEN 65/65 at llvm20 and llvm22;
pytest core (55) + test_rocke (231) + smem liveness (13) + golden gate (3) pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@aledudek
aledudek force-pushed the users/barkocot/rocke-common-lds branch from cb87e77 to c2ea5ba Compare July 27, 2026 14:29
aledudek and others added 5 commits July 27, 2026 19:32
Rebase of PR #8844 (users/barkocot/rocke-common-lds) onto the reorganized
develop (rocKE/ -> rocke/platform, Python/ -> platform/python, Cpp/ ->
platform/cpp).

Only the common-LDS-allocation half of the original PR is applied here: the
fp32/bf16 cshuffle half the branch also carried is already upstream on develop
(store ops, trunc_f32_to_bf16, dtype_d epilogues), so re-applying it was skipped
to avoid duplicate definitions.

Contents (net vs develop): a single liveness-packed addrspace(3) smem pool with
per-allocation byte offsets, DFS-preorder live-interval analysis + greedy
interval packing (non-interfering A/B and cshuffle-C share the pool: pool =
max(overlapping) instead of sum), and a per-(block,allocation) base-pointer
cache so a non-zero-offset segment emits one byte-GEP per block. Mirrored in
both the Python (core/lower_llvm.py) and C++ (cpp/core/lower_llvm/*) engines;
conv implicit-GEMM LDS budget switched to max(A/B, C); new
test_smem_liveness_packing.py; golden re-blessed for both llvm flavors.

Validation on the rebased tree: byte-identity GREEN 65/65 at llvm20 and llvm22;
pytest core (55) + test_rocke (231) + smem liveness (13) + golden gate (3) pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a workgroup barrier before the first cshuffle C store (C aliases A/B
in the shared pool but the double-buffered/prefetched tail MFMA had no
barrier before the epilogue), guard the packer against expanding a reused
slot over a still-live slot, and extend inner-loop liveness to the
enclosing loop. Both engines; golden re-blessed.
Add an opt-in `cshuffle_no_alias` flag to the cshuffle epilogue that lets a
kernel trade LDS/occupancy for latency, addressing yraparti's review request
on the common-LDS-pool work.

By default (False, unchanged) the smem-pool packer aliases the cshuffle C tile
onto the A/B staging bytes (pool = max(ab, c)), which requires the epilogue
"step-0 reuse barrier" between the main loop's last A/B read and the first C
write. This is best for large tiles (more occupancy, throughput-bound).

When True, C gets its own exclusive LDS bytes (pool = ab + c) and the step-0
barrier is elided -- the two are safe together precisely because C no longer
overlaps A/B. This lowers latency for small tiles. The kernel name gains a
`noalc` tag so aliased and un-aliased kernels are distinct.

Mechanism: a new shared `SmemType.exclusive` bit (kept out of the type name so
default output is byte-identical) tells the packer to give that allocation its
own byte range (skip slot reuse + sentinel-lock it). The flag sets it on the C
allocation and elides the barrier, in both the inline GEMM epilogue and the
shared CShuffleEpilogue (conv). The bit round-trips through serialization via an
`exclusive` op attr so the cpp/both backends match the python backend.

Implemented in both engines (byte-identical) with golden coverage for both
variants:
- Python: core/ir.py, core/lower_llvm.py (packer), core/ir_serialize.py,
  helpers/{epilogues,tensor_view}.py, instances/common/{gemm_universal,
  conv_implicit_gemm}.py, examples/common/universal_gemm_verify.py.
- C++: ir.h, core/ir/{core_types,ir_tile,serialize}.cpp,
  core/lower_llvm/core.cpp, helpers/{epilogues,tensor_view}.cpp + headers,
  instances/common/{gemm_epilogue,gemm_spec,conv_implicit_gemm_conv_epilogue,
  conv_implicit_gemm_conv_spec_descriptors}.cpp,
  include/rocke/{instance_gemm_universal,instance_conv_implicit_gemm}.h.
- Tests: parity emitters gemm_emit.{py,c} + conv_implicit_gemm_emit.{py,c}
  gain an alias-off config; rocke_ir_parity_harness.py adds an alias-off
  representative case; golden re-blessed (additions only, no existing sha
  changed).

Verification:
- Byte-identity GREEN at llvm20 and llvm22 (65 families; gemm and
  conv_implicit_gemm configs=11 bad=0 -- the new no-alias config included).
- Golden gate passes; diff is additions-only.
- On-GPU numeric (gfx942) bad=0 for both aliased and no-alias cshuffle GEMM.

Default behavior is byte-identical; only the opt-in path is new.
- conv is_valid_spec sized LDS as max(ab, c) unconditionally, which
  under-counts when cshuffle_no_alias=True (C gets its own bytes, so the
  real footprint is ab + c) and could accept an over-budget kernel. Compute
  per case: max(ab, c) when aliased, ab + c when no-alias. Mirrored in the
  C++ engine (conv_implicit_gemm_conv_spec_descriptors.cpp).
- rocke_smem_type now validates `exclusive` is 0 or 1 (error otherwise)
  instead of silently coercing; the Python SmemType already defaults it.
- Add regression tests: no-alias elides the reuse barrier + marks C
  exclusive, and the conv LDS budget flips accept/reject with the flag.
@aledudek
aledudek force-pushed the users/barkocot/rocke-common-lds branch from 111ca44 to af6e177 Compare July 27, 2026 19:33
@bartekxk
bartekxk merged commit eea3da6 into develop Jul 27, 2026
25 checks passed
@bartekxk
bartekxk deleted the users/barkocot/rocke-common-lds branch July 27, 2026 21:21
AnaghaRaoAMD added a commit that referenced this pull request Jul 30, 2026
…smoke (#10128)

## Summary

The attention build-smoke tests stopped at `lower_kernel_to_llvm` (IR
text) and never invoked comgr — so a kernel that lowers to valid-looking
IR but is **rejected at codegen** (LDS over the arch cap) passed CI.
That gap let the fp16 D128 prefill LDS-overflow regression (**81920 B >
gfx942's 64 KB cap**, PR #8844) reach `develop` and run on **8 perf
sweeps** before the dashboard caught it as `rocke_status: error`.

This adds a comgr build + resource-budget net to
`library/tests/test_attention_builds.py`. **No torch, no GPU** — comgr
targets each arch via its triple and the arch resource caps come from a
static `ArchTarget` table, so it runs on any comgr/LLVM CI host
regardless of the box's own GPU.

- `_compile_or_skip` — runs the full comgr pipeline; **skips** only when
the toolchain is unavailable, **propagates** a real `ComgrError` (a
genuine codegen failure fails the test).
- `_assert_resources_fit` — reads resource metadata from the emitted
HSACO via `llvm-readelf` and asserts the kernel fits budget.
- Wired both into the 2D/3D tiled compile-smoke tests, plus a curated
`(arch × dtype × head_dim)` matrix built through the **real selector**
(`_tiled_spec_from_problem`) so it exercises the actual dispatch
decision, not a hand-guessed geometry.
- **Also covers the gfx950 dense flash-attn prefill kernel**
(`build_attention_dense`) — a *separate* builder/ABI (not routed through
the unified 2D-tiled path, its own `rocke_dense_tflops` dashboard
column) that the matrix would otherwise never touch. Covers the default
and persistent variants; it is LDS-heavy (tunable V pad via
`ROCKE_DENSE_VPAD`), so a live over-budget risk on its own.

### Why two checks, not just "call comgr"
The two resource limits fail **differently** under comgr, so the net
covers both:
- **LDS over the arch cap** → comgr **hard-errors** at codegen (the
compile step catches it). The explicit cap assert fires one step earlier
with a readable diagnostic and catches soft growth toward the ceiling.
- **VGPR over-subscription** → comgr does **not** fail — it **spills to
scratch** and compiles anyway, then loses occupancy. A pass/fail compile
check is blind to this, so `_assert_resources_fit` asserts
`scratch_bytes == 0` (arch-agnostic no-spill signal).

## Risk assessment

**Low.** Test-only change (+197 lines, one file); no product code
touched. The comgr half is gated: on a host without the toolchain the
added assertions **skip** rather than fail.

> ⚠️ **CI dependency:** any lane that builds rocKE already has comgr
(the runtime links `libamd_comgr` — it is a hard build dependency), so
comgr availability is **not** the concern. The gap is coverage: the
existing superbuild lane runs `ctest` scoped to `platform/tests` and not
`library/tests`[yet], where these tests live. A follow-up CI change is
needed to run `library/tests` on the existing rocKE build lane. The
`_compile_or_skip` skip path is a courtesy for local dev on a
mismatched-toolchain box (per `TESTING.md`); optionally assert
comgr-present on the CI lane as belt-and-suspenders so a misconfigured
lane can't silently skip. This PR is the test payload; the CI wiring is
separate.

## Testing summary

Verified locally on gfx942 (MI300A), ROCm 7.2 (llvm22), under a
**torch-free** venv:
- Touched tests pass green: 5 tests / 7 subtests, comgr reached, HSACO
produced (incl. gfx950 tiled + dense cross-arch from a gfx942 box).
- Real resource data (not a model): gfx950 fp16 D128 tiled → VGPR 254,
LDS 98304 B (fits 163840 cap), scratch 0; gfx950 dense → VGPR 218, LDS
75776 B, scratch 0.
- The LDS assert **fires** on a simulated over-budget cap → would have
caught #8844 (81920 > 65536 on gfx942).
- No regressions introduced: the other pre-existing failures in this
file reproduce on clean `develop` (an LLVM-flavor/arch env mismatch),
unaffected by this change.

## Testing checklist

- [ ] Run `pytest library/tests/test_attention_builds.py` on a
comgr-capable CI lane (no torch required)
- [ ] Confirm the new
`test_tiled_2d_shipped_geometries_compile_and_fit_budget` and
`test_gfx950_dense_prefill_compiles_and_fits_budget` subtests execute
(not skip) on that lane
- [ ] Confirm the 2D/3D tiled compile-smoke tests reach comgr (not skip)

## Technical changes

- `_compile_or_skip(kernel, *, arch)` and `_assert_resources_fit(art, *,
arch, kernel_name)` helpers.
- `test_unified_attention_2d_tiled_kernel_compiles` /
`test_unified_attention_3d_tiled_kernel_compiles`: additionally compile
through comgr + assert resource budget.
- New `test_tiled_2d_shipped_geometries_compile_and_fit_budget`: curated
shipped-geometry matrix over `(arch × dtype × head_dim)`.
- New `test_gfx950_dense_prefill_compiles_and_fits_budget`: gfx950 dense
kernel, default + persistent variants.
Changes are behind the feature flag HIPKERNELPROVIDER_ENABLE_ROCKE which
is defaulted to OFF
AICK-1831
🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude <noreply@anthropic.com>
cgmillette added a commit that referenced this pull request Oct 20, 2026
The differential golden had gone stale: 627 of its 1240 LLVM IR entries and
537 of its 1206 canonical IR entries no longer matched the tree, because
nothing ran the check. Recorded afresh in the new format, with the Python
reference emitters, from a tree whose two-engine gate is green for all 82
families:

- ll at llvm20, llvm22 and llvm23: 82 families, 1518 configs per flavor;
  1615 at llvm23, where gfx1250_lowering has configs that need LLVM 23 and
  are refused below it.
- ir_canonical: 81 families, 1598 configs (one family's emitter has no IR
  mode). Canonical IR was checked to be identical at llvm20 and llvm23.

What the re-bless absorbs: every mismatch that could be traced is an
intended code generation change that did not re-bless this file -- the
shared LDS pool (#8844, about 425 configs), the ds.swizzle declaration
(#9583, about 177), renamed values in the attention transpose reads (#9133,
57), the smaller LDS pool (#10112, about 39), the flat work group size of
one-wave wave32 kernels (#12710, about 39), #9220 (6) and the conv changes
of #13109 (6). 441 entries were already stale at the start of the history
available here and cannot be attributed; no unexplained change was found
after it. The two repeated keys (conv_direct_grouped,
conv_wgrad_workspace_reduce), which hid their newer copies, are gone.

It also takes in the code generation changes merged since then, each
traced by recording the file at the commit and at its parent:

- the magic division sum emitted without signed-overflow poison (#13802):
  215 configs in each of the four sections, in 19 families --
  attention_unified 6, attention_unified_stress 32, conv_direct_grouped
  16, conv_implicit_gemm 26, conv_implicit_gemm_dgrad 31,
  conv_implicit_gemm_wgrad 42, deep_fused_conv_pool 2,
  gfx1201_deep_fused_conv_pool 5, gfx950_deep_fused_conv_pool 5,
  dynamic_helpers 2, fmha_splitkv_decode 6, img2col 5, moe_fused_mega 5,
  moe_gemm_fused 6, moe_sorting 6, pooling 4, sage_attention 4,
  sparse_attention 6, transpose 6;
- attention decoding on gfx942 and gfx950 (#13492): 23 changed and 25 new
  configs per section in attention_strided_kv, gfx942_attention_tiled_3d
  and gfx950_attention_tiled_3d;
- wave_all on wave32 (#13896): 18 gfx1151_wmma_fmha_fwd configs per LLVM
  flavor, canonical IR unchanged;
- new configs: the barrier_fence family (#13575, 10) and one
  conv_implicit_gemm_dgrad config (#13782).

Co-Authored-By: Claude <noreply@anthropic.com>
cgmillette added a commit that referenced this pull request Oct 26, 2026
The differential golden had gone stale, because nothing ran the check:
675 of its 1116 LLVM IR entries (compared at llvm20) and 608 of its 1089
canonical IR entries no longer match the tree. Recorded afresh in the new
format, with the Python reference emitters, from a tree whose two-engine
gate is green at llvm20, llvm22 and llvm23:

- ll at llvm20 and llvm22: 82 families, 1474 configs per flavor. At
  llvm23: 83 families, 1578 configs, where gfx1250_lowering has configs
  that need LLVM 23 and are refused below it, and every
  conv_direct_dgrad_wmma config needs LLVM 23.
- ir_canonical: 82 families, 1561 configs (conv_abi's emitter has no IR
  mode). Canonical IR was checked to be identical at llvm20 and llvm23.

What the re-bless absorbs: every mismatch that could be traced is an
intended code generation change that did not re-bless this file -- the
shared LDS pool (#8844, about 425 configs), the ds.swizzle declaration
(#9583, about 177), renamed values in the attention transpose reads (#9133,
57), the smaller LDS pool (#10112, about 39), the flat work group size of
one-wave wave32 kernels (#12710, about 39), #9220 (6) and the conv changes
of #13109 (6). 441 entries were already stale at the start of the history
available here and cannot be attributed; no unexplained change was found
after it. The two repeated keys (conv_direct_grouped,
conv_wgrad_workspace_reduce), which hid their newer copies, are gone.

It also takes in the code generation changes merged since then, each
traced by recording the file at the commit and at its parent; the count is
per section unless a flavor is named:

- the magic division sum emitted without signed-overflow poison (#13802):
  215 configs in 19 families -- attention_unified 6,
  attention_unified_stress 32, conv_direct_grouped 16, conv_implicit_gemm
  26, conv_implicit_gemm_dgrad 31, conv_implicit_gemm_wgrad 42,
  deep_fused_conv_pool 2, gfx1201_deep_fused_conv_pool 5,
  gfx950_deep_fused_conv_pool 5, dynamic_helpers 2, fmha_splitkv_decode 6,
  img2col 5, moe_fused_mega 5, moe_gemm_fused 6, moe_sorting 6, pooling 4,
  sage_attention 4, sparse_attention 6, transpose 6;
- attention decoding on gfx942 and gfx950 (#13492): 23 changed and 25 new
  configs in attention_strided_kv, gfx942_attention_tiled_3d and
  gfx950_attention_tiled_3d;
- the f64 follow-ups (#13887): 3 changed and 1 new elementwise config, 5
  changed reduce configs;
- MX scale and fp6 decoding (#13885): 6 mx_gemm configs;
- the gfx950 conv dgrad speed-up (#13075): 36 new conv_direct_dgrad_ext
  configs; 22 changed, 15 new and 2 removed conv_implicit_gemm_dgrad
  configs; 2 changed and 3 new conv_direct_grouped configs; 2 new
  dynamic_helpers and 3 new target_intrinsics configs;
- removing the ds.read.tr16.b128 declaration (#14054): gfx1250_lowering,
  100 changed and 2 removed configs at llvm23 and in canonical IR, 5
  changed, 2 new and 4 removed at llvm20 and llvm22;
- optional LSE output on unified split-KV attention (#13955): 18
  gfx942_attention_tiled_3d and 16 gfx950_attention_tiled_3d configs per
  LLVM flavor, canonical IR unchanged;
- the running softmax max in 3D split-KV (#14276): 14
  attention_strided_kv, 18 gfx942_attention_tiled_3d and 16
  gfx950_attention_tiled_3d configs;
- depthwise 7x7 and grouped wgrad on batched MFMA (#14157): 7 changed, 10
  new and 1 removed conv_direct_grouped configs;
- the retirement of wmma_fmha_fwd (#13897): the gfx1151_wmma_fmha_fwd (157
  configs) and gfx1201_wmma_fmha_fwd (12) families are gone;
- new configs, every existing hash unchanged: the barrier_fence family
  (#13575, 10), one conv_implicit_gemm_dgrad config (#13782), 14
  conv_direct_grouped configs for the split-C and grouped LDS-halo kernels
  (#13993), one config each in fmha_splitkv_decode, sage_attention and
  sparse_attention (#13876), 23 conv_direct_grouped configs for the direct
  wgrad kernels (#13990), 2 conv_implicit_gemm_dgrad configs for
  patch-embedding dgrad (#13988), 6 f64_mma configs (#13794), 5
  conv_direct_grouped and 7 conv_implicit_gemm configs for depthwise and
  grouped conv forward (#13987), and the conv_direct_dgrad_wmma family (7
  configs, llvm23 and canonical IR only, #14156).

#13885, #13955 and #14276 updated the old file's entries for their
changes; the new llvm20 and canonical IR sections reproduce every one of
those hashes.

Co-Authored-By: Claude <noreply@anthropic.com>
cgmillette added a commit that referenced this pull request Oct 26, 2026
The differential golden had gone stale, because nothing ran the check:
675 of its 1116 LLVM IR entries (compared at llvm20) and 608 of its 1089
canonical IR entries no longer match the tree. Recorded afresh in the new
format, with the Python reference emitters, from a tree whose two-engine
gate is green at llvm20, llvm22 and llvm23:

- ll at llvm20 and llvm22: 82 families, 1474 configs per flavor. At
  llvm23: 83 families, 1578 configs, where gfx1250_lowering has configs
  that need LLVM 23 and are refused below it, and every
  conv_direct_dgrad_wmma config needs LLVM 23.
- ir_canonical: 82 families, 1561 configs (conv_abi's emitter has no IR
  mode). Canonical IR was checked to be identical at llvm20 and llvm23.

What the re-bless absorbs: every mismatch that could be traced is an
intended code generation change that did not re-bless this file -- the
shared LDS pool (#8844, about 425 configs), the ds.swizzle declaration
(#9583, about 177), renamed values in the attention transpose reads (#9133,
57), the smaller LDS pool (#10112, about 39), the flat work group size of
one-wave wave32 kernels (#12710, about 39), #9220 (6) and the conv changes
of #13109 (6). 441 entries were already stale at the start of the history
available here and cannot be attributed; no unexplained change was found
after it. The two repeated keys (conv_direct_grouped,
conv_wgrad_workspace_reduce), which hid their newer copies, are gone.

It also takes in the code generation changes merged since then, each
traced by recording the file at the commit and at its parent; the count is
per section unless a flavor is named:

- the magic division sum emitted without signed-overflow poison (#13802):
  215 configs in 19 families -- attention_unified 6,
  attention_unified_stress 32, conv_direct_grouped 16, conv_implicit_gemm
  26, conv_implicit_gemm_dgrad 31, conv_implicit_gemm_wgrad 42,
  deep_fused_conv_pool 2, gfx1201_deep_fused_conv_pool 5,
  gfx950_deep_fused_conv_pool 5, dynamic_helpers 2, fmha_splitkv_decode 6,
  img2col 5, moe_fused_mega 5, moe_gemm_fused 6, moe_sorting 6, pooling 4,
  sage_attention 4, sparse_attention 6, transpose 6;
- attention decoding on gfx942 and gfx950 (#13492): 23 changed and 25 new
  configs in attention_strided_kv, gfx942_attention_tiled_3d and
  gfx950_attention_tiled_3d;
- the f64 follow-ups (#13887): 3 changed and 1 new elementwise config, 5
  changed reduce configs;
- MX scale and fp6 decoding (#13885): 6 mx_gemm configs;
- the gfx950 conv dgrad speed-up (#13075): 36 new conv_direct_dgrad_ext
  configs; 22 changed, 15 new and 2 removed conv_implicit_gemm_dgrad
  configs; 2 changed and 3 new conv_direct_grouped configs; 2 new
  dynamic_helpers and 3 new target_intrinsics configs;
- removing the ds.read.tr16.b128 declaration (#14054): gfx1250_lowering,
  100 changed and 2 removed configs at llvm23 and in canonical IR, 5
  changed, 2 new and 4 removed at llvm20 and llvm22;
- optional LSE output on unified split-KV attention (#13955): 18
  gfx942_attention_tiled_3d and 16 gfx950_attention_tiled_3d configs per
  LLVM flavor, canonical IR unchanged;
- the running softmax max in 3D split-KV (#14276): 14
  attention_strided_kv, 18 gfx942_attention_tiled_3d and 16
  gfx950_attention_tiled_3d configs;
- depthwise 7x7 and grouped wgrad on batched MFMA (#14157): 7 changed, 10
  new and 1 removed conv_direct_grouped configs;
- the retirement of wmma_fmha_fwd (#13897): the gfx1151_wmma_fmha_fwd (157
  configs) and gfx1201_wmma_fmha_fwd (12) families are gone;
- new configs, every existing hash unchanged: the barrier_fence family
  (#13575, 10), one conv_implicit_gemm_dgrad config (#13782), 14
  conv_direct_grouped configs for the split-C and grouped LDS-halo kernels
  (#13993), one config each in fmha_splitkv_decode, sage_attention and
  sparse_attention (#13876), 23 conv_direct_grouped configs for the direct
  wgrad kernels (#13990), 2 conv_implicit_gemm_dgrad configs for
  patch-embedding dgrad (#13988), 6 f64_mma configs (#13794), 5
  conv_direct_grouped and 7 conv_implicit_gemm configs for depthwise and
  grouped conv forward (#13987), and the conv_direct_dgrad_wmma family (7
  configs, llvm23 and canonical IR only, #14156).

#13885, #13955 and #14276 updated the old file's entries for their
changes; the new llvm20 and canonical IR sections reproduce every one of
those hashes.

Co-Authored-By: Claude <noreply@anthropic.com>
cgmillette added a commit that referenced this pull request Oct 27, 2026
…13974)

## Motivation

The differential golden
(`tests/instances/differential/golden/llvm_gfx_all.json`) is the only
tripwire for an output change in about 55 parity families that the
representative golden does not cover (norms, reduce, transpose, fmha,
wmma GEMMs, flatmm, mx_gemm, ...). The byte-identity gate cannot see a
change made the same way in both engines; this file can. But nothing ran
its check, and on develop 627 of its 1240 LLVM IR entries and 537 of its
1206 canonical IR entries no longer matched.

The checker itself made it easy to go stale:
- Entries carried no LLVM flavor, so blesses from hosts of different
flavors mixed and no flavor matched the file.
- A re-bless limited with `--only` replaced the whole section; one did,
and the pasted-back copy left two repeated JSON keys whose older copies
silently won.
- A check limited with `--only` reported every other family missing.
- The check needed the C++ build although it compares Python reference
hashes only.

ISSUE ID : AICK-2654

## Technical Details

Three commits.

1. **The checker** (`fix`)
- New `golden_store.py` owns the file (schema 2): LLVM IR hashes in one
section per flavor (`llvm20`, `llvm22`, `llvm23`), canonical IR in one
section (produced before lowering; identical across flavors, checked).
Loading refuses repeated keys and the old format. A filtered record
replaces only the families it ran; a filtered check compares only those.
- `run_diff.py` pins the flavor for every golden run (`--flavor`, else
`ROCKE_LLVM_FLAVOR`, else the host's) and exports it to both emitters.
- `--python-only` records or checks from the Python reference emitters
alone: no C++ engine, any flavor on any host, a list of flavors in one
run. It produces the same hashes as the two-engine run (all 82 families,
checked).
- The canonical hash has one definition
(`python_batch_worker.canonical_digest`), shared by the batch worker,
which now computes it in parallel, and `run_diff.py`.
- `run_diff.py` finds the rocke and library packages in an installed
test tree too.
2. **The re-bless** (`test`, data only): recorded afresh at llvm20,
llvm22, llvm23 and canonical IR from a tree whose two-engine gate is
green for all 82 families (re-recorded after rebasing onto develop with
#13802, whose magic-division change moved 215 configs in 19 families in
every section). Every mismatch that could be traced is an intended code
generation change that did not re-bless this file (#8844, #9583, #9133,
#10112, #12710, #9220, #13109); 441 entries were already stale at the
start of the history available and cannot be attributed; no unexplained
change was found after it. The commit message has the breakdown.
3. **CI** (`test`): two quick-tier CTest entries run the Python-only
check from the installed test tree, `rocke_differential_golden_ll` (all
three flavors) and `rocke_differential_golden_canonical`. The install
ships only what that check needs from `tests/instances/differential`;
the rest still needs the C++ build and stays out.

The golden README and the troubleshooting entry describe the check and
re-bless commands. No emitter or IR changes.

## Test Plan

- `test_golden_store.py`: repeated keys refused by name; the old format
refused with the way out; flavor sections separate; a full record
replaces, a filtered record merges; a filtered check ignores other
families; new configs alone pass.
- The existing differential tests (gate verdict, scaled MFMA batch)
still pass.
- Python-only vs two-engine record at llvm20: identical hashes for all
82 families; canonical IR identical at llvm20 and llvm23.
- The check passes on the re-blessed tree and fails (1058 changed
configs) when the lowerer's output is altered.
- Installed tree (provider layout, staged install): both CTest entries
pass, and fail when the installed lowerer's output is altered.
- Byte identity at llvm20, llvm22, llvm23; ctest; full platform suite.

## Test Result

- All of the above pass.
- Installed CTest time: about 15 s (LLVM IR, three flavors) and 13 s
(canonical IR).
- Byte identity green at llvm20, llvm22 and llvm23; ctest passes; full
platform suite passes with no failures; pre-commit passes.

## Submission Checklist

- [ ] Look over the contributing guidelines at
https://github.com/ROCm/TheRock/blob/main/GOVERNANCE.md#pull-requests.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants