Skip to content

[CUDA] Adopt multi-staged buffers in examples - #2836

Merged
Yongqi-Zhuo merged 2 commits into
tile-ai:mainfrom
Yongqi-Zhuo:sliced-kernels
Aug 4, 2026
Merged

[CUDA] Adopt multi-staged buffers in examples#2836
Yongqi-Zhuo merged 2 commits into
tile-ai:mainfrom
Yongqi-Zhuo:sliced-kernels

Conversation

@Yongqi-Zhuo

@Yongqi-Zhuo Yongqi-Zhuo commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator

Thanks to my series of PRs (#2380, #2452, #2785), you can write arbitrary slicing expressions to express the multiple stages of a multi-version buffer. So you no longer need to write brittle code that declares multiple versions of a buffer for multiple times, just to pass it to the exact same T.copy, T.gemm, etc., due to lack of support for slicing. Use slicing and indexing. That's all.

Summary

  • Reworked CUDA examples to use configurable, stage-indexed shared-memory buffers.
  • Consolidated repeated query, KV, output, and barrier allocations.
  • Replaced stage-specific branches with indexed multi-stage pipelines.
  • Updated MLA, sparse attention, flash attention, and vertical/slash attention examples.
  • Added sliced-buffer support for WGMMA and TCGEN05 descriptor initialization.
  • Updated layout-remapped access handling in lower_tile_op.cc.
  • Removed explicit unrolling flags from the SM120 NVFP4 benchmark.
  • Added regression coverage for static non-zero-origin WGMMA slices.

Testing

  • Run regression performance testing twice for the updated CUDA kernels.
  • Run the new WGMMA sliced-buffer test.

C++ style / lint notes

  • The PR changes C++ code in src/transform/lower_tile_op.cc.
  • Review the changes against docs/developer_guide/cpp_style.md.
  • The “C++ API Style Audit (warning only)” CI step is relevant.
  • Treat TLCPP003 and TLCPP004 findings as advisory unless they indicate a clear API, FFI, or maintainability risk.
  • Separate correctness and build failures from warning-only style findings.

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown

👋 Hi! Thank you for contributing to the TileLang project.

Please remember to run pre-commit run --all-files in the root directory of the project to ensure your changes are properly linted and formatted. This will help ensure your contribution passes the format check.

We appreciate you taking this step! Our team will review your contribution, and we look forward to your awesome work! 🚀

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR generalizes attention kernels from fixed double buffers to stage-indexed storage and synchronization. It consolidates MLA query and output buffers. It also updates layout remapping and CUDA descriptor generation for static and dynamic shared-memory slice offsets, adds sliced WGMMA coverage, and adjusts three benchmark unroll directives.

Changes

Staged attention kernels

Layer / File(s) Summary
MLA decode staged buffering
examples/deepseek_mla/example_mla_decode_ws.py
Split and non-split decode paths use unified query/output storage, staged full-width KV storage, and indexed readiness and reuse barriers.
Sparse MLA combined storage
examples/deepseek_v32/sparse_mla_bwd.py, examples/deepseek_v32/sparse_mla_fwd.py
Forward and backward kernels use combined query buffers with explicit main and tail slices for loads and GEMMs.
Sparse MLA staged pipelines
examples/deepseek_v32/sparse_mla_fwd_pipelined.py, examples/deepseek_v32/sparse_mla_fwd_seesaw.py, examples/deepseek_v32/README.md
The pipelines default to two stages, use stage-indexed buffers and barriers, and iterate producer and consumer paths over staged KV data.
Other staged attention kernels
examples/flash_attention_sm100/gqa_fwd_bshd.py, examples/flash_attention_sm100/mha_fwd_bshd.py, examples/minference/example_vertical_slash_sparse_attn.py
Flash attention and vertical-slash attention use stage-indexed K/V buffers and remove duplicated fixed-buffer branches.

Static slice descriptor lowering

Layer / File(s) Summary
Layout-aware access remapping
src/transform/lower_tile_op.cc
RemapAccessIndices validates ranks and shapes, preserves multidimensional indices, applies offsets, and handles pointer-access remapping.
CUDA descriptor offset handling
tilelang/cuda/intrinsics/macro/tcgen05_macro_generator.py, tilelang/cuda/intrinsics/macro/wgmma_macro_generator.py, tilelang/cuda/intrinsics/macro/wgmma_sp_macro_generator.py
Descriptor generators apply static slice offsets to base pointers and retain dynamic offsets as descriptor adjustments.
Static sliced WGMMA coverage
testing/python/language/test_tilelang_language_wgmma_gemm.py
A CUDA test validates static slice pointers, descriptor initialization, execution, and sliced matrix multiplication results.

SM120 benchmark loop directives

Layer / File(s) Summary
K-loop unroll updates
maint/gemm/gemm_sm120/benchmark_sm120_nvfp4_blockscaled_gemm.py
Three producer and consumer K-tile loops retain unroll factor 1 without explicit=False.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Suggested reviewers: leiwang1999

Sequence Diagram(s)

sequenceDiagram
  participant Producer
  participant StageBarriers
  participant AttentionConsumer
  participant OutputBuffer
  Producer->>StageBarriers: signal stage readiness after staged KV copy
  AttentionConsumer->>StageBarriers: wait for selected stage
  AttentionConsumer->>AttentionConsumer: compute attention and accumulate output
  AttentionConsumer->>StageBarriers: release the processed stage
  AttentionConsumer->>OutputBuffer: write the combined output
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adopting multi-stage buffers in CUDA examples.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Yongqi-Zhuo
Yongqi-Zhuo requested a review from LeiWang1999 August 1, 2026 19:29

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b7bdea6b5c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +440 to +442
for (size_t i = 0; i < indices.size(); ++i) {
multi_dim_indices.Set(
i, analyzer_->Simplify(indices[i] + multi_dim_indices[i]));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Carry offsets across buffer dimensions before layout mapping

When an address_of/tl.access_ptr already has nonzero multidimensional indices and a PTX intrinsic supplies an additional element offset, these lines add the offset's coordinates independently without propagating carries. For example, shape [M, 16], indices [r, 15], and offset 1 are mapped as [r, 16] instead of [r + 1, 0]; passing the out-of-range coordinate through layout->Forward can select the wrong shared-memory address and corrupt ptx_ldmatrix results. Normalize the combined linear position, as the previous implementation did, before applying the layout.

Useful? React with 👍 / 👎.

<< "The original buffer shape must match the layout input shape, but "
"got "
<< old_shape << " and " << input_shape;
ICHECK(relax::CanProveShapeEqual(new_shape, output_shape, analyzer_))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Allow the replicate dimension in remapped shared buffers

When a shared layout has replicate_extent > 1, makeBufferWithLayout intentionally prepends that replicate axis to new_buffer->shape, so new_shape cannot equal layout->OutputShape(). Any remapped access_ptr or address_of for such a buffer now fails this ICHECK during compilation even though this representation was previously supported; the validation and index construction need to account for the extra leading replicate dimension.

Useful? React with 👍 / 👎.

@Yongqi-Zhuo

Copy link
Copy Markdown
Collaborator Author

@regression-perf

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 4

🧹 Nitpick comments (5)
examples/minference/example_vertical_slash_sparse_attn.py (1)

245-279: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document or assert the two-stage assumption of this prefetch schedule.

The stage index now uses % num_stages, so the code reads as generic. The schedule is not generic: it primes exactly one block, issues one prefetch per iteration, and calls Compute with count=1. That leaves exactly one outstanding cp.async group, which is only correct for num_stages == 2. If someone raises num_stages, the extra buffers stay unused and the wait count stops matching the pipeline depth.

Add an assertion next to the num_stages definition so the constraint fails fast.

♻️ Proposed guard
     num_stages = 2
+    assert num_stages == 2, "the vertical-column prefetch schedule keeps one cp.async group in flight"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/minference/example_vertical_slash_sparse_attn.py` around lines 245 -
279, Add a fail-fast assertion beside the num_stages definition in the
surrounding sparse-attention setup, requiring exactly two stages to match the
prefetch schedule and Compute count=1 pipeline. Leave the existing
stage-indexing and prefetch logic unchanged.
tilelang/cuda/intrinsics/macro/wgmma_macro_generator.py (1)

427-512: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate static/dynamic slice-offset resolution logic.

The dynamic-slice, static-slice, and non-sliced branches for B_base_ptr repeat the same pattern used for A_base_ptr right below, and the identical block is duplicated again in tcgen05_macro_generator.py and wgmma_sp_macro_generator.py (6 occurrences total). Extract a shared helper, for example resolve_slice_base_ptr(base_ptr, slice_byte_offset) -> (base_ptr, is_sliced), and reuse it in all six locations to avoid divergent fixes over time.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tilelang/cuda/intrinsics/macro/wgmma_macro_generator.py` around lines 427 -
512, Extract the repeated slice-offset handling into a shared
`resolve_slice_base_ptr(base_ptr, slice_byte_offset)` helper that returns the
adjusted pointer and `is_sliced` flag, preserving dynamic, nonzero static, and
zero-offset behavior. Replace the duplicated branches in `init_wgmma_b_desc`,
`init_wgmma_a_desc`, and the corresponding four locations in the tcgen05 and
wgmma_sp macro generators with this helper.
tilelang/cuda/intrinsics/macro/tcgen05_macro_generator.py (1)

805-872: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate static/dynamic slice-offset resolution logic.

The dynamic-slice, static-slice, and non-sliced branches for B_base_ptr and A_base_ptr repeat the identical eight-line pattern for both descriptors in this file. The same pattern also appears in wgmma_macro_generator.py and wgmma_sp_macro_generator.py. Extract a shared helper, for example resolve_slice_base_ptr(base_ptr, slice_byte_offset) -> (base_ptr, is_sliced), and reuse it across all six call sites. This reduces the risk that a future fix to this logic is applied in some sites but missed in others.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tilelang/cuda/intrinsics/macro/tcgen05_macro_generator.py` around lines 805 -
872, Extract the repeated static, dynamic, and non-sliced offset handling into a
shared resolve_slice_base_ptr helper returning the adjusted base pointer and
is_sliced flag. Replace the duplicated logic in init_tcgen05_b_desc,
init_tcgen05_a_desc, and the corresponding four call sites in
wgmma_macro_generator.py and wgmma_sp_macro_generator.py, preserving existing
pointer adjustment and dynamic descriptor-offset behavior.
tilelang/cuda/intrinsics/macro/wgmma_sp_macro_generator.py (2)

353-362: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate static/dynamic slice-offset resolution logic.

Same pattern as Lines 196-210 in this file, and as wgmma_macro_generator.py / tcgen05_macro_generator.py. Reuse the same extracted helper here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tilelang/cuda/intrinsics/macro/wgmma_sp_macro_generator.py` around lines 353
- 362, Extract the repeated B slice-offset resolution in the current generator
into the shared helper already used by the analogous logic around lines 196-210
and in wgmma_macro_generator.py/tcgen05_macro_generator.py. Replace the inline
isinstance/static-offset branching around B_base_ptr with that helper while
preserving dynamic offsets, nonzero static pointer adjustment, and the
b_is_sliced result.

196-210: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate static/dynamic slice-offset resolution logic.

The A and B slice-offset branches here repeat the same static/dynamic/zero pattern used in wgmma_macro_generator.py and tcgen05_macro_generator.py. Extract a shared helper and reuse it here and at Lines 353-362 in this file.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tilelang/cuda/intrinsics/macro/wgmma_sp_macro_generator.py` around lines 196
- 210, Extract the repeated slice-offset resolution pattern into a shared
helper, covering dynamic offsets, nonzero static offsets, and zero offsets while
returning the resolved sliced state and base pointer. Replace the duplicated A/B
logic in the current block and the corresponding handling around lines 353-362,
and reuse the existing helper from wgmma_macro_generator.py and
tcgen05_macro_generator.py where applicable.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@examples/deepseek_mla/example_mla_decode_ws.py`:
- Around line 161-165: Add an assertion near the split producer’s num_stages = 2
definition in examples/deepseek_mla/example_mla_decode_ws.py:161-165 requiring
(seqlen_kv // num_split) to be divisible by block_N * num_stages, preventing
trailing stages beyond NI; the sibling non-split producer at
examples/deepseek_mla/example_mla_decode_ws.py:341-345 requires no direct change
because this shared guard protects both MLA decode producers.

In `@examples/deepseek_v32/README.md`:
- Line 157: Update the documented KV_shared allocation snippet in README.md to
use the combined feature width D + D_tail, matching the staged KV buffer
allocation in sparse_mla_fwd_pipelined.py. Keep the existing num_stages and BI
dimensions unchanged.

In `@src/transform/lower_tile_op.cc`:
- Around line 409-445: Update RemapAccessIndices to validate new_shape against
the buffer shape produced by makeBufferWithLayout, including shared-buffer
replicate_extent, rather than layout->OutputShape(). Preserve the existing
shape-equality validation and index remapping behavior for non-replicated
buffers.

In `@tilelang/language/loop.py`:
- Around line 285-293: Update the validation around the explicit and
unroll-factor handling in the loop unroll logic to also reject an existing
"pragma_unroll_factor" in annotations when explicit is true, including when
explicit is enabled by the explicit argument. Preserve the current
mutual-exclusion error for the unroll_factor argument and ensure conflicting
annotations are not emitted.

---

Nitpick comments:
In `@examples/minference/example_vertical_slash_sparse_attn.py`:
- Around line 245-279: Add a fail-fast assertion beside the num_stages
definition in the surrounding sparse-attention setup, requiring exactly two
stages to match the prefetch schedule and Compute count=1 pipeline. Leave the
existing stage-indexing and prefetch logic unchanged.

In `@tilelang/cuda/intrinsics/macro/tcgen05_macro_generator.py`:
- Around line 805-872: Extract the repeated static, dynamic, and non-sliced
offset handling into a shared resolve_slice_base_ptr helper returning the
adjusted base pointer and is_sliced flag. Replace the duplicated logic in
init_tcgen05_b_desc, init_tcgen05_a_desc, and the corresponding four call sites
in wgmma_macro_generator.py and wgmma_sp_macro_generator.py, preserving existing
pointer adjustment and dynamic descriptor-offset behavior.

In `@tilelang/cuda/intrinsics/macro/wgmma_macro_generator.py`:
- Around line 427-512: Extract the repeated slice-offset handling into a shared
`resolve_slice_base_ptr(base_ptr, slice_byte_offset)` helper that returns the
adjusted pointer and `is_sliced` flag, preserving dynamic, nonzero static, and
zero-offset behavior. Replace the duplicated branches in `init_wgmma_b_desc`,
`init_wgmma_a_desc`, and the corresponding four locations in the tcgen05 and
wgmma_sp macro generators with this helper.

In `@tilelang/cuda/intrinsics/macro/wgmma_sp_macro_generator.py`:
- Around line 353-362: Extract the repeated B slice-offset resolution in the
current generator into the shared helper already used by the analogous logic
around lines 196-210 and in wgmma_macro_generator.py/tcgen05_macro_generator.py.
Replace the inline isinstance/static-offset branching around B_base_ptr with
that helper while preserving dynamic offsets, nonzero static pointer adjustment,
and the b_is_sliced result.
- Around line 196-210: Extract the repeated slice-offset resolution pattern into
a shared helper, covering dynamic offsets, nonzero static offsets, and zero
offsets while returning the resolved sliced state and base pointer. Replace the
duplicated A/B logic in the current block and the corresponding handling around
lines 353-362, and reuse the existing helper from wgmma_macro_generator.py and
tcgen05_macro_generator.py where applicable.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: d1b39319-1029-40ec-b40c-7545902fc99c

📥 Commits

Reviewing files that changed from the base of the PR and between 6b81bb8 and b7bdea6.

📒 Files selected for processing (22)
  • examples/deepseek_mla/example_mla_decode_ws.py
  • examples/deepseek_v32/README.md
  • examples/deepseek_v32/sparse_mla_bwd.py
  • examples/deepseek_v32/sparse_mla_fwd.py
  • examples/deepseek_v32/sparse_mla_fwd_pipelined.py
  • examples/deepseek_v32/sparse_mla_fwd_seesaw.py
  • examples/flash_attention_sm100/gqa_fwd_bshd.py
  • examples/flash_attention_sm100/mha_fwd_bshd.py
  • examples/minference/example_vertical_slash_sparse_attn.py
  • maint/gemm/gemm_sm120/benchmark_sm120_nvfp4_blockscaled_gemm.py
  • src/backend/common/op/reduce.h
  • src/transform/loop_partition.cc
  • src/transform/lower_tile_op.cc
  • src/transform/unroll_loop.cc
  • testing/python/language/test_tilelang_language_wgmma_gemm.py
  • testing/python/transform/test_tilelang_transform_unroll_loop.py
  • tilelang/cuda/intrinsics/macro/tcgen05_macro_generator.py
  • tilelang/cuda/intrinsics/macro/wgmma_macro_generator.py
  • tilelang/cuda/intrinsics/macro/wgmma_sp_macro_generator.py
  • tilelang/cuda/pipeline.py
  • tilelang/language/loop.py
  • tilelang/language/tir/ir.py
💤 Files with no reviewable changes (1)
  • src/transform/loop_partition.cc

Comment thread examples/deepseek_mla/example_mla_decode_ws.py
# ... load KV into buffer 1
T.cp_async_barrier_noinc(bar_k_1_ready[0])
num_stages = 2
KV_shared = T.alloc_shared([num_stages, BI, D], dtype)

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Align the documented KV buffer shape with the kernel.

sparse_mla_fwd_pipelined.py line 83 allocates the staged KV buffer with the combined feature width D + D_tail. The snippet shows only D.

📝 Proposed fix
 num_stages = 2
-KV_shared = T.alloc_shared([num_stages, BI, D], dtype)
+KV_shared = T.alloc_shared([num_stages, BI, D + D_tail], dtype)
 bar_k_ready = T.alloc_barrier(arrive_count=[128] * num_stages)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/deepseek_v32/README.md` at line 157, Update the documented KV_shared
allocation snippet in README.md to use the combined feature width D + D_tail,
matching the staged KV buffer allocation in sparse_mla_fwd_pipelined.py. Keep
the existing num_stages and BI dimensions unchanged.

Comment on lines +409 to 445
Array<PrimExpr> RemapAccessIndices(const Array<PrimExpr> &indices,
const Array<PrimExpr> &old_shape,
const Array<PrimExpr> &new_shape,
const Layout &layout,
const Optional<PrimExpr> &offset) {
ICHECK_EQ(indices.size(), old_shape.size())
<< "The access rank must match the original buffer rank, but got "
<< indices << " and " << old_shape;
ICHECK(!old_shape.empty())
<< "Layout-remapped access pointers do not support scalar buffers";
const Array<PrimExpr> input_shape = layout->InputShape();
const Array<PrimExpr> output_shape = layout->OutputShape();
ICHECK(relax::CanProveShapeEqual(old_shape, input_shape, analyzer_))
<< "The original buffer shape must match the layout input shape, but "
"got "
<< old_shape << " and " << input_shape;
ICHECK(relax::CanProveShapeEqual(new_shape, output_shape, analyzer_))
<< "The remapped buffer shape must match the layout output shape, but "
"got "
<< new_shape << " and " << output_shape;

// Delinearize only the additional offset. Keeping the original indices in
// multidimensional form preserves slice-local expressions for the layout.
PrimExpr remaining_offset =
analyzer_->Simplify(offset.value_or(make_zero(indices[0].dtype())));
Array<PrimExpr> multi_dim_indices;
for (int i = static_cast<int>(old_shape.size()) - 1; i >= 0; --i) {
multi_dim_indices.insert(multi_dim_indices.begin(),
floormod(remaining_offset, old_shape[i]));
remaining_offset = floordiv(remaining_offset, old_shape[i]);
}
for (size_t i = 0; i < indices.size(); ++i) {
multi_dim_indices.Set(
i, analyzer_->Simplify(indices[i] + multi_dim_indices[i]));
}
return layout->Forward(multi_dim_indices);
}

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.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Look for replicate_extent handling and any tests/examples exercising it together with access-ptr remapping.
rg -n "replicate_extent" src/transform/lower_tile_op.cc
rg -n "IsSharedBuffer" src -g '*.cc' -g '*.h'
rg -n "kLayoutMap" src -g '*.cc' -g '*.h' -C2

Repository: tile-ai/tilelang

Length of output: 13113


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== lower_tile_op.cc relevant sections =="
sed -n '1,180p' src/transform/lower_tile_op.cc
echo "--"
sed -n '300,470p' src/transform/lower_tile_op.cc
echo "--"
sed -n '470,560p' src/transform/lower_tile_op.cc

echo "== replicate_extent usages in repo =="
rg -n "replicate_extent|MakeBufferWithLayoutBuffer|makeBufferWithLayout" src test tests examples 2>/dev/null || true

echo "== buffer access remapping functions names =="
rg -n "RemapAccessIndices|HandleAccessPtrAndOffset|address_of|tvm_access_ptr|tl::access_ptr|access_ptr" src/transform/lower_tile_op.cc src -g '*.cc' -g '*.h' -C1

Repository: tile-ai/tilelang

Length of output: 50373


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Find kLayoutMap writers and layout_remap usages =="
rg -n "layout_remap_|Set\(attr::kLayoutMap|kLayoutMap|LowerTileOpPass::" src test tests examples 2>/dev/null || true

echo "== Layout inference annotations context =="
sed -n '700,760p' src/transform/layout_inference.cc
sed -n '1260,1310p' src/transform/layout_inference.cc

echo "== Access-ptr lowering transformation context =="
sed -n '1,130p' src/transform/lower_access_ptr.cc

Repository: tile-ai/tilelang

Length of output: 11098


Avoid crashing tvm_access_ptr remapping on shared buffers with replicate_extent.

makeBufferWithLayout adds the shared-buffer replicate_extent into the new buffer shape, but RemapAccessIndices still checks new_shape against layout->OutputShape(). A shared buffer that needs replicate_extent > 1 can reach this check through tvm_address_ptr/tl::access_ptr lowering and shared-LDMA/stores, so the ICHECK would abort. Check against the makeBufferWithLayout shape produced for this buffer instead of the raw layout output shape.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/transform/lower_tile_op.cc` around lines 409 - 445, Update
RemapAccessIndices to validate new_shape against the buffer shape produced by
makeBufferWithLayout, including shared-buffer replicate_extent, rather than
layout->OutputShape(). Preserve the existing shape-equality validation and index
remapping behavior for non-replicated buffers.

Comment thread tilelang/language/loop.py Outdated
Comment on lines 285 to 293
if explicit:
annotations["pragma_unroll_explicit"] = True
else:
explicit = annotations.get("pragma_unroll_explicit", False)

if unroll_factor is not None:
# check pragma_unroll_explicit must be False
if annotations.get("pragma_unroll_explicit", True):
raise ValueError("pragma_unroll_explicit must be True when unroll_factor is not None")
if explicit:
raise ValueError("T.unroll's explicit and unroll_factor params are mutually exclusive.")
annotations.update({"pragma_unroll_factor": unroll_factor})

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reject a factor supplied through annotations when explicit mode is enabled.

T.unroll(..., explicit=True, annotations={"pragma_unroll_factor": 1}) passes this validation. It emits both annotations. The unroll pass then treats the loop as explicitly expandable instead of preserving the factor-based loop.

Proposed fix
-    if unroll_factor is not None:
-        if explicit:
-            raise ValueError("T.unroll's explicit and unroll_factor params are mutually exclusive.")
+    if explicit and ("pragma_unroll_factor" in annotations or unroll_factor is not None):
+        raise ValueError("T.unroll's explicit and unroll_factor params are mutually exclusive.")
+
+    if unroll_factor is not None:
         annotations.update({"pragma_unroll_factor": unroll_factor})
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if explicit:
annotations["pragma_unroll_explicit"] = True
else:
explicit = annotations.get("pragma_unroll_explicit", False)
if unroll_factor is not None:
# check pragma_unroll_explicit must be False
if annotations.get("pragma_unroll_explicit", True):
raise ValueError("pragma_unroll_explicit must be True when unroll_factor is not None")
if explicit:
raise ValueError("T.unroll's explicit and unroll_factor params are mutually exclusive.")
annotations.update({"pragma_unroll_factor": unroll_factor})
if explicit:
annotations["pragma_unroll_explicit"] = True
else:
explicit = annotations.get("pragma_unroll_explicit", False)
if explicit and ("pragma_unroll_factor" in annotations or unroll_factor is not None):
raise ValueError("T.unroll's explicit and unroll_factor params are mutually exclusive.")
if unroll_factor is not None:
annotations.update({"pragma_unroll_factor": unroll_factor})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tilelang/language/loop.py` around lines 285 - 293, Update the validation
around the explicit and unroll-factor handling in the loop unroll logic to also
reject an existing "pragma_unroll_factor" in annotations when explicit is true,
including when explicit is enabled by the explicit argument. Preserve the
current mutual-exclusion error for the unroll_factor argument and ensure
conflicting annotations are not emitted.

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown

Performance Regression Test Report

Triggered by: @Yongqi-Zhuo
Workflow run: https://github.com/tile-ai/tilelang/actions/runs/30715096064

Results

File Original Latency Current Latency Speedup
example_topk 0.0451099 0.0484499 0.931062
example_warp_specialize_gemm_softpipe_stage2 0.0154611 0.0157204 0.983501
example_dequant_gemm_fp4_hopper 0.531752 0.535413 0.993163
example_tilelang_block_sparse_attn 0.00568723 0.00572602 0.993225
example_gqa_sink_bwd_bhsd 0.0250711 0.0252229 0.993981
example_dequant_gemv_fp16xint4 0.017346 0.0174491 0.994093
example_mha_sink_fwd_bhsd_sliding_window 0.00970637 0.00976365 0.994133
example_mhc_pre 0.115498 0.116109 0.994744
example_tilelang_gemm_fp8_2xAcc 0.0678898 0.0681986 0.995472
example_dequant_gemm_bf16_fp4_hopper 0.267212 0.268402 0.995565
example_tilelang_sparse_gqa_decode_varlen_indice 0.0107684 0.010816 0.995601
example_warp_specialize_gemm_copy_0_gemm_1 0.0235584 0.0236426 0.996438
example_convolution 0.581793 0.583633 0.996848
example_group_per_split_token_cast_to_fp8 0.00563898 0.00565542 0.997094
example_vertical_slash_sparse_attn 0.135592 0.135848 0.998113
example_linear_attn_bwd 0.0970032 0.0971688 0.998295
example_gqa_decode 0.0305406 0.0305768 0.998813
example_mha_fwd_varlen 0.0205747 0.0205956 0.998984
example_gemm_intrinsics 0.0201595 0.0201792 0.999025
example_gqa_bwd_tma_reduce_varlen 0.027882 0.0279085 0.999052
example_mha_sink_bwd_bhsd_sliding_window 0.0262299 0.0262503 0.999225
topk_selector 0.0272518 0.0272648 0.999525
example_mha_fwd_bhsd 0.00688391 0.00688659 0.999611
example_gemv 0.149094 0.149115 0.999859
example_tilelang_sparse_gqa_decode_varlen_mask 0.0281882 0.0281873 1.00003
example_mhc_post 0.0657431 0.0657397 1.00005
example_gqa_bwd 0.0288053 0.0288006 1.00016
example_mha_inference 0.0329392 0.032933 1.00019
example_tilelang_gemm_splitk 0.590899 0.590753 1.00025
example_gqa_fwd_bshd 0.0297636 0.0297482 1.00052
example_tilelang_gemm_splitk_vectorize_atomicadd 0.584045 0.583662 1.00066
example_convolution_autotune 0.591641 0.591184 1.00077
example_mha_fwd_bshd 0.0147992 0.0147869 1.00083
example_blocksparse_gemm 0.0117699 0.0117601 1.00083
example_elementwise_add 0.0691593 0.0691 1.00086
sparse_mla_fwd_pipelined 0.0347231 0.0346881 1.00101
example_warp_specialize_gemm_copy_1_gemm_0 0.0154715 0.0154547 1.00109
example_fusedmoe_tilelang 0.0764967 0.0764091 1.00115
example_linear_attn_fwd 0.0228342 0.0227957 1.00169
example_mha_sink_bwd_bhsd 0.040905 0.0408307 1.00182
example_per_token_cast_to_fp8 0.00432997 0.00432178 1.00189
example_dequant_gemm_bf16_mxfp4_hopper 0.257883 0.257345 1.00209
example_dynamic 0.38829 0.387268 1.00264
example_tilelang_gemm_fp8 0.171362 0.170792 1.00334
example_mha_sink_fwd_bhsd 0.0098773 0.009843 1.00348
example_tilelang_nsa_fwd 0.00406779 0.00405362 1.00349
example_dequant_gemm_w4a8 2.6894 2.67972 1.00361
sparse_mla_bwd 0.136947 0.136396 1.00404
example_mha_bwd_bshd 0.0139685 0.013907 1.00442
example_tilelang_nsa_decode 0.00419681 0.00417735 1.00466
example_warp_specialize_gemm_barrierpipe_stage2 0.0249031 0.0247857 1.00474
block_sparse_attn_tilelang 0.00618925 0.00615818 1.00505
sparse_mla_fwd 0.0533145 0.0530378 1.00522
example_gqa_sink_bwd_bhsd_sliding_window 0.0152655 0.0151838 1.00538
example_gemm 0.0148425 0.0147627 1.0054
example_mha_bwd_bhsd 0.0140679 0.0139363 1.00944
fp8_lighting_indexer 0.0120442 0.0119134 1.01098
example_mla_decode 0.302064 0.297332 1.01592

Artifacts

  • regression_result.png (speedup plot) is attached as a workflow artifact. Download it from the workflow run page above.

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (1)
testing/python/layout/test_tilelang_cute.py (1)

1042-1042: 🚀 Performance & Scalability | 🔵 Trivial

Run CUDA performance regression tests before merge.

The assertion validates generated TMA code and runtime correctness, but it does not detect throughput regressions from looped TMA loads. Compare this path with the pre-change baseline.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@testing/python/layout/test_tilelang_cute.py` at line 1042, Update the TMA
coverage around the generated-source assertion to include a CUDA performance
regression check for looped TMA loads, comparing the measured throughput against
the pre-change baseline before merge. Preserve the existing tma_load generation
and runtime-correctness assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@testing/python/layout/test_tilelang_cute.py`:
- Line 1042: Update the TMA coverage around the generated-source assertion to
include a CUDA performance regression check for looped TMA loads, comparing the
measured throughput against the pre-change baseline before merge. Preserve the
existing tma_load generation and runtime-correctness assertions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: cbe03a26-c442-4d26-a639-0526ac954100

📥 Commits

Reviewing files that changed from the base of the PR and between b7bdea6 and 3eba453.

📒 Files selected for processing (1)
  • testing/python/layout/test_tilelang_cute.py

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (1)
tilelang/tools/pass_visualizer/core.py (1)

126-130: 🚀 Performance & Scalability | 🔵 Trivial

Run CUDA performance regression tests for the new pass order.

UnrollLoop and the added Simplify now run before PipelinePlanning. Test explicit, disabled, dynamic, and factor-based unroll cases, including warp-specialized kernels.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tilelang/tools/pass_visualizer/core.py` around lines 126 - 130, Update the
pass sequence around the stages list to retain UnrollLoop and the preceding
Simplify before PipelinePlanning, then run CUDA performance regression tests
covering explicit, disabled, dynamic, and factor-based unroll configurations,
including warp-specialized kernels.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tilelang/tools/pass_visualizer/core.py`:
- Around line 126-130: Update the pass sequence around the stages list to retain
UnrollLoop and the preceding Simplify before PipelinePlanning, then run CUDA
performance regression tests covering explicit, disabled, dynamic, and
factor-based unroll configurations, including warp-specialized kernels.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: faa685b8-e943-4c33-a2c7-8e16d9bb7e68

📥 Commits

Reviewing files that changed from the base of the PR and between 3eba453 and fd263f4.

📒 Files selected for processing (24)
  • examples/deepseek_mla/example_mla_decode_ws.py
  • examples/deepseek_v32/README.md
  • examples/deepseek_v32/sparse_mla_bwd.py
  • examples/deepseek_v32/sparse_mla_fwd.py
  • examples/deepseek_v32/sparse_mla_fwd_pipelined.py
  • examples/deepseek_v32/sparse_mla_fwd_seesaw.py
  • examples/flash_attention_sm100/gqa_fwd_bshd.py
  • examples/flash_attention_sm100/mha_fwd_bshd.py
  • examples/minference/example_vertical_slash_sparse_attn.py
  • maint/gemm/gemm_sm120/benchmark_sm120_nvfp4_blockscaled_gemm.py
  • src/backend/common/op/reduce.h
  • src/transform/loop_partition.cc
  • src/transform/lower_tile_op.cc
  • src/transform/unroll_loop.cc
  • testing/python/language/test_tilelang_language_wgmma_gemm.py
  • testing/python/layout/test_tilelang_cute.py
  • testing/python/transform/test_tilelang_transform_unroll_loop.py
  • tilelang/cuda/intrinsics/macro/tcgen05_macro_generator.py
  • tilelang/cuda/intrinsics/macro/wgmma_macro_generator.py
  • tilelang/cuda/intrinsics/macro/wgmma_sp_macro_generator.py
  • tilelang/cuda/pipeline.py
  • tilelang/language/loop.py
  • tilelang/language/tir/ir.py
  • tilelang/tools/pass_visualizer/core.py
💤 Files with no reviewable changes (1)
  • src/transform/loop_partition.cc
🚧 Files skipped from review as they are similar to previous changes (22)
  • maint/gemm/gemm_sm120/benchmark_sm120_nvfp4_blockscaled_gemm.py
  • tilelang/cuda/pipeline.py
  • tilelang/language/loop.py
  • tilelang/cuda/intrinsics/macro/wgmma_macro_generator.py
  • testing/python/layout/test_tilelang_cute.py
  • tilelang/language/tir/ir.py
  • tilelang/cuda/intrinsics/macro/wgmma_sp_macro_generator.py
  • examples/deepseek_v32/sparse_mla_bwd.py
  • tilelang/cuda/intrinsics/macro/tcgen05_macro_generator.py
  • testing/python/transform/test_tilelang_transform_unroll_loop.py
  • examples/flash_attention_sm100/mha_fwd_bshd.py
  • src/transform/unroll_loop.cc
  • examples/minference/example_vertical_slash_sparse_attn.py
  • src/transform/lower_tile_op.cc
  • examples/deepseek_v32/README.md
  • examples/deepseek_v32/sparse_mla_fwd.py
  • testing/python/language/test_tilelang_language_wgmma_gemm.py
  • examples/deepseek_mla/example_mla_decode_ws.py
  • src/backend/common/op/reduce.h
  • examples/flash_attention_sm100/gqa_fwd_bshd.py
  • examples/deepseek_v32/sparse_mla_fwd_seesaw.py
  • examples/deepseek_v32/sparse_mla_fwd_pipelined.py

LeiWang1999
LeiWang1999 previously approved these changes Aug 3, 2026
@Yongqi-Zhuo

Copy link
Copy Markdown
Collaborator Author

@regression-perf

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown

Performance Regression Test Report

Triggered by: @Yongqi-Zhuo
Workflow run: https://github.com/tile-ai/tilelang/actions/runs/30841361310

Results

File Original Latency Current Latency Speedup
example_warp_specialize_gemm_copy_1_gemm_0 0.015512 0.0156628 0.990371
example_dequant_gemm_bf16_mxfp4_hopper 0.252621 0.254963 0.990814
example_dequant_gemm_fp4_hopper 0.529854 0.533815 0.992579
example_gemv 0.148283 0.1492 0.993856
example_mla_decode 0.299549 0.301333 0.99408
block_sparse_attn_tilelang 0.00615296 0.00618811 0.994319
example_mhc_post 0.0656857 0.0659833 0.99549
sparse_mla_fwd_pipelined 0.034708 0.0348533 0.995831
example_warp_specialize_gemm_softpipe_stage2 0.0154203 0.0154684 0.996886
example_linear_attn_fwd 0.0228214 0.0228903 0.996994
sparse_mla_bwd 0.136381 0.136606 0.998355
example_mha_fwd_bshd 0.0147914 0.0148157 0.998355
example_warp_specialize_gemm_barrierpipe_stage2 0.0247771 0.0248099 0.998677
example_convolution_autotune 0.590993 0.591618 0.998945
topk_selector 0.0272511 0.0272694 0.999326
example_elementwise_add 0.0690785 0.0691248 0.999331
example_tilelang_gemm_fp8 0.170121 0.17023 0.999359
example_convolution 0.584385 0.58464 0.999564
example_gqa_sink_bwd_bhsd 0.0250775 0.0250883 0.999569
example_gemm_intrinsics 0.0201525 0.0201603 0.999611
example_tilelang_gemm_splitk_vectorize_atomicadd 0.584323 0.584541 0.999627
example_mha_sink_fwd_bhsd 0.00985754 0.00985883 0.99987
example_gqa_bwd_tma_reduce_varlen 0.027865 0.0278655 0.999984
example_tilelang_gemm_fp8_2xAcc 0.0678269 0.0678278 0.999987
example_tilelang_sparse_gqa_decode_varlen_indice 0.0108298 0.0108297 1.00001
example_tilelang_gemm_splitk 0.591853 0.591829 1.00004
example_tilelang_sparse_gqa_decode_varlen_mask 0.0282538 0.0282497 1.00014
example_dynamic 0.388316 0.388241 1.00019
example_gqa_sink_bwd_bhsd_sliding_window 0.0152821 0.0152789 1.00021
example_tilelang_nsa_decode 0.00418491 0.00418375 1.00028
example_mhc_pre 0.115456 0.1154 1.00049
example_mha_sink_fwd_bhsd_sliding_window 0.00976955 0.00976398 1.00057
example_warp_specialize_gemm_copy_0_gemm_1 0.0235667 0.0235514 1.00065
example_mha_fwd_bhsd 0.00689333 0.00688847 1.00071
example_fusedmoe_tilelang 0.0765915 0.0765361 1.00072
example_dequant_gemv_fp16xint4 0.0173704 0.0173508 1.00113
example_mha_inference 0.0330075 0.0329671 1.00123
example_mha_sink_bwd_bhsd_sliding_window 0.0262776 0.0262428 1.00132
example_per_token_cast_to_fp8 0.0043232 0.00431538 1.00181
example_dequant_gemm_bf16_fp4_hopper 0.269012 0.268448 1.0021
example_gqa_fwd_bshd 0.0297685 0.0297032 1.0022
example_mha_sink_bwd_bhsd 0.040946 0.0408554 1.00222
example_blocksparse_gemm 0.0117111 0.0116833 1.00239
example_linear_attn_bwd 0.0970012 0.0967528 1.00257
example_tilelang_block_sparse_attn 0.00573014 0.0057154 1.00258
example_group_per_split_token_cast_to_fp8 0.00561802 0.00560104 1.00303
example_gqa_decode 0.0305754 0.0304814 1.00308
example_vertical_slash_sparse_attn 0.135836 0.135416 1.0031
example_mha_bwd_bhsd 0.0140503 0.0140067 1.00311
example_tilelang_nsa_fwd 0.00406512 0.00404724 1.00442
fp8_lighting_indexer 0.0120231 0.0119693 1.00449
sparse_mla_fwd 0.0532001 0.0529472 1.00478
example_gqa_bwd 0.0289306 0.0287907 1.00486
example_mha_bwd_bshd 0.0139623 0.0138815 1.00583
example_dequant_gemm_w4a8 2.69513 2.67933 1.0059
example_mha_fwd_varlen 0.0206853 0.0205445 1.00685
example_gemm 0.0148713 0.0147621 1.0074
example_topk 0.0455463 0.0392608 1.1601

Artifacts

  • regression_result.png (speedup plot) is attached as a workflow artifact. Download it from the workflow run page above.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

♻️ Duplicate comments (1)
examples/deepseek_mla/example_mla_decode_ws.py (1)

162-165: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Restore the NI divisibility guard before using both stages.

The NI calculations at Line 74 and Line 255 use T.ceildiv(..., block_N), but the loops at Line 161 and Line 341 always execute both stage values. When NI is odd, the final stage computes tile NI at Line 165 and Line 345. This reads beyond KV and K_pe, and the consumers process data that was not loaded.

Add the guard near Line 34 so both kernels require complete two-stage tiles.

Proposed fix
     num_stages = 2
+    assert (seqlen_kv // num_split) % (block_N * num_stages) == 0

This is the same unresolved finding from the previous review; the current code still lacks the guard.

Also applies to: 342-345

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/deepseek_mla/example_mla_decode_ws.py` around lines 162 - 165,
Restore the NI divisibility guard near the shared kernel setup so both decode
kernels reject configurations where NI is not divisible by 2. Ensure the guard
covers the stage loops in the kernels containing the stage and kv_indices
calculations, preventing the final incomplete stage from reading or consuming
out-of-range KV and K_pe data.
🧹 Nitpick comments (1)
maint/gemm/gemm_sm120/benchmark_sm120_nvfp4_blockscaled_gemm.py (1)

162-162: 🚀 Performance & Scalability | 🔵 Trivial | 🏗️ Heavy lift

Run the SM120 performance regression before merging.

The default T.unroll behavior preserves pragma_unroll_explicit=False. Compare correctness and throughput before and after this change on the target SM120 GPU. Include two regression runs to detect code-generation or performance variance.

Also applies to: 212-212, 250-250

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@maint/gemm/gemm_sm120/benchmark_sm120_nvfp4_blockscaled_gemm.py` at line 162,
Run the SM120 benchmark regression for the loops using T.unroll in the relevant
GEMM paths, comparing correctness and throughput before and after the change
with pragma_unroll_explicit=False preserved. Execute two runs on the target
SM120 GPU for each version to identify code-generation or performance variance,
and verify no correctness or throughput regression before merging.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@examples/deepseek_mla/example_mla_decode_ws.py`:
- Around line 91-92: Ensure the stage loop around T.unroll in the MLA decode
path is explicitly unrolled by passing explicit=True, or otherwise guard the
CUDA backend against an unexpanded constant loop while preserving the loop’s
kUnrolled behavior.

---

Duplicate comments:
In `@examples/deepseek_mla/example_mla_decode_ws.py`:
- Around line 162-165: Restore the NI divisibility guard near the shared kernel
setup so both decode kernels reject configurations where NI is not divisible by
2. Ensure the guard covers the stage loops in the kernels containing the stage
and kv_indices calculations, preventing the final incomplete stage from reading
or consuming out-of-range KV and K_pe data.

---

Nitpick comments:
In `@maint/gemm/gemm_sm120/benchmark_sm120_nvfp4_blockscaled_gemm.py`:
- Line 162: Run the SM120 benchmark regression for the loops using T.unroll in
the relevant GEMM paths, comparing correctness and throughput before and after
the change with pragma_unroll_explicit=False preserved. Execute two runs on the
target SM120 GPU for each version to identify code-generation or performance
variance, and verify no correctness or throughput regression before merging.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 24212b76-a8a0-46af-ab33-6533872d8a1c

📥 Commits

Reviewing files that changed from the base of the PR and between 50c2afe and 78541fa.

📒 Files selected for processing (4)
  • examples/deepseek_mla/example_mla_decode_ws.py
  • examples/deepseek_v32/sparse_mla_fwd_pipelined.py
  • examples/deepseek_v32/sparse_mla_fwd_seesaw.py
  • maint/gemm/gemm_sm120/benchmark_sm120_nvfp4_blockscaled_gemm.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • examples/deepseek_v32/sparse_mla_fwd_seesaw.py
  • examples/deepseek_v32/sparse_mla_fwd_pipelined.py

Comment on lines +91 to +92
for stage in T.unroll(num_stages):
T.barrier_wait(bar_k_ready[stage], (i_i & 1))

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.

🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="examples/deepseek_mla/example_mla_decode_ws.py"

test "$(rg -o 'T\.unroll\(num_stages\)' "$file" | wc -l)" -eq 6

rg -n -C 6 \
  'def unroll|pragma_unroll_explicit|explicit_unroll|UnrollLoop' \
  tilelang src

rg -n -C 8 \
  'compute_gmma_descriptor|slice_byte_offset|increase_descriptor_offset' \
  tilelang

Repository: tile-ai/tilelang

Length of output: 50373


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== candidate file outline/stat =="
wc -l "examples/deepseek_mla/example_mla_decode_ws.py"
ast-grep outline "examples/deepseek_mla/example_mla_decode_ws.py" --view compact || true

echo "== relevant unroll loops and barrier/operand context =="
nl -ba "examples/deepseek_mla/example_mla_decode_ws.py" | sed -n '70,175p;255,355p'

echo "== focused TIL loop definitions =="
nl -ba "tilelang/language/tir/ir.py" | sed -n '1,130p'
nl -ba "tilelang/language/loop.py" | sed -n '220,305p'

echo "== UnrollLoop pass config relevant lines =="
nl -ba "src/transform/unroll_loop.cc" | sed -n '40,230p;350,385p'
nl -ba "src/transform/loop_partition.cc" | sed -n '235,275p'

echo "== deterministic model of explicit annotation placement =="
python3 - <<'PY'
from pathlib import Path
p = Path('tilelang/language/tir/ir.py')
text = p.read_text()
start = text.index('def unroll(')
end = text.index('def thread_binding(')
body = text[start:end]
print("unroll body:")
print(body)
print("explicit_true_unroll_in_body=", 'return _ir.unroll(start=start, stop=stop, explicit=explicit, annotations=annotations)' in body)
PY

Repository: tile-ai/tilelang

Length of output: 486


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== candidate file relevant context =="
awk 'NR>=70 && NR<=175 {printf "%5d    %s\n", NR, $0} NR>=255 && NR<=355 {printf "%5d    %s\n", NR, $0}' examples/deepseek_mla/example_mla_decode_ws.py

echo "== unroll API definitions =="
awk '/^def unroll\(/,/^def thread_binding\(/' tilelang/language/tir/ir.py
awk '/^def unroll\(/,/^def .*load_array\(/' tilelang/language/loop.py | head -n 120

echo "== unroll pass config relevant lines =="
awk 'NR>=40 && NR<=230 {printf "%5d    %s\n", NR, $0} NR>=350 && NR<=385 {printf "%5d    %s\n", NR, $0}' src/transform/unroll_loop.cc
awk 'NR>=235 && NR<=275 {printf "%5d    %s\n", NR, $0}' src/transform/loop_partition.cc

Repository: tile-ai/tilelang

Length of output: 28887


Keep the stage loop explicitly unrolled.

T.unroll(num_stages) emits pragma_unroll_explicit: False, so UnrollLoop may only leave the loop marked as kUnrolled instead of expanding it. Guard the resulting CUDA backend against an unexpanded constant loop, or use T.unroll(num_stages, explicit=True).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/deepseek_mla/example_mla_decode_ws.py` around lines 91 - 92, Ensure
the stage loop around T.unroll in the MLA decode path is explicitly unrolled by
passing explicit=True, or otherwise guard the CUDA backend against an unexpanded
constant loop while preserving the loop’s kUnrolled behavior.

@Yongqi-Zhuo

Copy link
Copy Markdown
Collaborator Author

@regression-perf

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown

Performance Regression Test Report

Triggered by: @Yongqi-Zhuo
Workflow run: https://github.com/tile-ai/tilelang/actions/runs/30844866465

Results

File Original Latency Current Latency Speedup
example_mla_decode 0.298846 0.302075 0.989309
sparse_mla_bwd 0.136143 0.137046 0.993411
example_convolution 0.581239 0.584943 0.993667
example_per_token_cast_to_fp8 0.00429728 0.00432207 0.994265
example_dequant_gemm_bf16_fp4_hopper 0.267149 0.26858 0.994675
fp8_lighting_indexer 0.0119736 0.0120359 0.994829
block_sparse_attn_tilelang 0.00616121 0.00619023 0.995312
example_warp_specialize_gemm_copy_1_gemm_0 0.0154131 0.0154803 0.995658
example_mha_fwd_bshd 0.0147694 0.0148227 0.996404
example_tilelang_gemm_splitk 0.589179 0.591241 0.996512
example_vertical_slash_sparse_attn 0.136308 0.136657 0.997445
example_mha_fwd_varlen 0.0206122 0.0206645 0.997466
example_dynamic 0.387563 0.388531 0.99751
example_gemm_intrinsics 0.0201435 0.0201841 0.997987
example_mhc_post 0.065609 0.0657319 0.998129
example_mha_sink_fwd_bhsd 0.00981318 0.00983085 0.998202
example_gemm 0.0147927 0.0148186 0.998253
example_group_per_split_token_cast_to_fp8 0.00562588 0.00563339 0.998666
example_blocksparse_gemm 0.0117347 0.011747 0.998958
example_tilelang_nsa_decode 0.0041834 0.00418743 0.999037
example_mha_fwd_bhsd 0.00689331 0.00689923 0.999142
example_warp_specialize_gemm_copy_0_gemm_1 0.0235433 0.0235601 0.999284
example_mha_sink_fwd_bhsd_sliding_window 0.00973422 0.00973965 0.999442
example_convolution_autotune 0.591298 0.591589 0.999508
example_tilelang_gemm_fp8_2xAcc 0.0678288 0.0678618 0.999513
example_fusedmoe_tilelang 0.0764975 0.0765297 0.99958
example_gqa_bwd_tma_reduce_varlen 0.0278687 0.0278759 0.999742
example_mhc_pre 0.115465 0.115481 0.999861
example_mha_sink_bwd_bhsd_sliding_window 0.0261874 0.0261878 0.999985
example_tilelang_sparse_gqa_decode_varlen_indice 0.0107633 0.0107635 0.999987
example_dequant_gemm_w4a8 2.6792 2.67922 0.999991
example_tilelang_nsa_fwd 0.00405507 0.00405493 1.00004
example_mha_inference 0.0329786 0.0329742 1.00013
topk_selector 0.0272581 0.0272477 1.00038
example_gemv 0.148114 0.148057 1.00038
example_elementwise_add 0.069141 0.0691115 1.00043
example_mha_bwd_bhsd 0.0140225 0.014015 1.00054
example_warp_specialize_gemm_barrierpipe_stage2 0.0247449 0.0247219 1.00093
example_tilelang_gemm_splitk_vectorize_atomicadd 0.584892 0.584329 1.00096
example_tilelang_block_sparse_attn 0.00570567 0.00569934 1.00111
sparse_mla_fwd_pipelined 0.034769 0.0347288 1.00116
example_gqa_sink_bwd_bhsd_sliding_window 0.0152802 0.0152619 1.0012
example_gqa_sink_bwd_bhsd 0.0250562 0.0250149 1.00165
example_tilelang_sparse_gqa_decode_varlen_mask 0.0282637 0.028216 1.00169
example_gqa_decode 0.0306146 0.0305465 1.00223
example_linear_attn_fwd 0.0229317 0.0228732 1.00256
example_gqa_bwd 0.0288804 0.028805 1.00262
example_gqa_fwd_bshd 0.0298336 0.0297452 1.00297
example_dequant_gemm_bf16_mxfp4_hopper 0.258183 0.257202 1.00381
example_tilelang_gemm_fp8 0.17147 0.170761 1.00415
example_mha_bwd_bshd 0.0139212 0.0138576 1.00459
sparse_mla_fwd 0.0533205 0.0530742 1.00464
example_mha_sink_bwd_bhsd 0.0409705 0.0407498 1.00542
example_dequant_gemm_fp4_hopper 0.535171 0.532092 1.00579
example_dequant_gemv_fp16xint4 0.017453 0.0173409 1.00646
example_linear_attn_bwd 0.0973612 0.096619 1.00768
example_warp_specialize_gemm_softpipe_stage2 0.0156104 0.0154829 1.00824
example_topk 0.0413883 0.0329988 1.25424

Artifacts

  • regression_result.png (speedup plot) is attached as a workflow artifact. Download it from the workflow run page above.

@Yongqi-Zhuo
Yongqi-Zhuo merged commit caf6944 into tile-ai:main Aug 4, 2026
5 of 6 checks passed
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.

2 participants