Skip to content

[BugFix] Fix LayoutInference divide-by-zero on non-power-of-two broadcast#2469

Merged
LeiWang1999 merged 2 commits into
tile-ai:mainfrom
Chennesxu:fix/layout-inference-div-zero
Jul 3, 2026
Merged

[BugFix] Fix LayoutInference divide-by-zero on non-power-of-two broadcast#2469
LeiWang1999 merged 2 commits into
tile-ai:mainfrom
Chennesxu:fix/layout-inference-div-zero

Conversation

@Chennesxu

@Chennesxu Chennesxu commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Summary

LayoutInference can crash with an internal Divide by zero when lowering a valid column-broadcast fragment access whose tile width is not a power of two.

d_local = T.alloc_fragment((BN,), T.float32)
x = T.alloc_fragment((BM, BN), T.float32)

for i, j in T.Parallel(BM, BN):
    x[i, j] = d_local[j] * 2.0

Root cause

get_unused_iters tracks the portion of an iterator already covered by existing IterSplitExprs using:

expected_lower_factor = splits[j]->lower_factor * splits[j]->extent;

For non-power-of-two tile widths, iter split normalization may over-approximate the covered span. For example, an iterator with extent 48 may be represented by a split span that covers up to 64.

The old logic only treated exact equality as full coverage:

expected_lower_factor == mark->extent

So when the covered span became larger than the mark extent, it was treated as incomplete coverage. get_unused_iters then synthesized a leftover split with zero extent, which later normalized into a divide-
by-zero.

Fix

Keep the split-chain progression intact, but treat proven over-coverage as full coverage at the final leftover check:

bool covers_full_iter =
    analyzer->CanProveEqual(expected_lower_factor, mark->extent) ||
    analyzer->CanProve(expected_lower_factor > mark->extent);

If the consumed span is equal to or greater than the original iterator extent, there is no remaining iterator space to synthesize, so get_unused_iters does not emit a zero-extent leftover split.

The fix also simplifies the newly advanced expected_lower_factor before the final coverage check.

Tested

  • Added regression coverage in testing/python/transform/test_tilelang_transform_layout_inference.py:
    • host-lowering crash regression with block_n = 24, 40, 48, 64, 96
    • CUDA numerical regression with block_n = 32, 48, 96, checking that the column broadcast matches D * 2
  • Verified locally on a TITAN RTX host:
    • testing/python/transform/test_tilelang_transform_layout_inference.py
    • testing/python/layout

Fixes #2394

Summary

  • Fixed a LayoutInference compile-time crash in leftover-iterator handling by treating proven over-coverage as full coverage, preventing zero-extent leftover splits that could lead to a divide-by-zero during lowering of valid non-power-of-two column-broadcast fragments (issue #2394).
  • Added regression tests covering both lowering and CUDA numerical correctness for column-broadcast fragment kernels across several non-power-of-two block_n values (including newly added 24 and 40), and verified that numerical output matches the reference broadcasted result.

C++ style / lint notes

  • This PR touches C++ layout inference logic in src/layout/utils.cc, so it is relevant to docs/developer_guide/cpp_style.md.
  • No public API/FFI changes were introduced.
  • The “C++ API Style Audit (warning only)” CI step should still be expected to run for this C++ change; this PR does not introduce any specific new warning-only findings based on the provided diff.

@github-actions

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 Jun 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e12ee787-4be8-4f87-96cc-1f68a67e87b0

📥 Commits

Reviewing files that changed from the base of the PR and between 9d34ce1 and a8be115.

📒 Files selected for processing (1)
  • testing/python/transform/test_tilelang_transform_layout_inference.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • testing/python/transform/test_tilelang_transform_layout_inference.py

📝 Walkthrough

Walkthrough

get_unused_iters in src/layout/utils.cc now simplifies expected_lower_factor and treats provable over-coverage of mark->extent as full coverage, skipping leftover split emission. New tests cover lowering and CUDA correctness for column broadcast fragment kernels across non-power-of-two block_n values.

Changes

LayoutInference divide-by-zero fix and tests

Layer / File(s) Summary
get_unused_iters over-coverage fix
src/layout/utils.cc
get_unused_iters now simplifies expected_lower_factor and accepts provable over-coverage as full coverage, skipping the leftover split in that case.
Column broadcast fragment lowering and values
testing/python/transform/test_tilelang_transform_layout_inference.py
Adds torch import, a parameterized lowering test, a @tl.jit kernel factory, and a CUDA-only correctness test for fragment-based column broadcast across several block_n values.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related issues

Suggested reviewers

  • LeiWang1999

Poem

🐇 I hopped through tiles with a cheerful grin,
Spotted a zero where splits had been.
Now over-coverage skips that faulty plight,
And broadcast fragments compile just right!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 matches the main change: fixing a LayoutInference divide-by-zero for non-power-of-two broadcasts.
Linked Issues check ✅ Passed The fix and regression tests address issue #2394 by preventing zero-extent leftover iterators and verifying non-power-of-two broadcast cases.
Out of Scope Changes check ✅ Passed The changes stay focused on the reported LayoutInference bug and its tests, with no obvious unrelated additions.
✨ 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.

@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

🤖 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 `@testing/python/transform/test_tilelang_transform_layout_inference.py`:
- Around line 225-236: Update test_column_broadcast_fragment_values to include
the original failing CUDA repro widths 24 and 40 in the block_n parametrization,
in addition to the existing 32, 48, and 96 cases. Keep the rest of the test
logic unchanged so the regression coverage in this tilelang transform layout
inference test exercises the reported non-power-of-two shapes.
🪄 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

Run ID: c103e9b1-f761-4c8a-ac18-b0dcef9c4d03

📥 Commits

Reviewing files that changed from the base of the PR and between ed00dfc and 9d34ce1.

📒 Files selected for processing (2)
  • src/layout/utils.cc
  • testing/python/transform/test_tilelang_transform_layout_inference.py

@LeiWang1999

Copy link
Copy Markdown
Member

@regression-perf

@github-actions

Copy link
Copy Markdown

Performance Regression Test Report

Triggered by: @LeiWang1999
Workflow run: https://github.com/tile-ai/tilelang/actions/runs/28357306283

Results

File Original Latency Current Latency Speedup
example_topk 30.0392 39.0637 0.768981
example_mha_sink_bwd_bhsd_sliding_window 0.0379896 0.038633 0.983346
example_dequant_gemm_bf16_mxfp4_hopper 0.355491 0.359938 0.987647
example_vertical_slash_sparse_attn 0.15908 0.160987 0.988158
example_mhc_pre 0.14348 0.144572 0.992446
example_mha_bwd_bhsd 0.0297975 0.0299544 0.994759
sparse_mla_fwd_pipelined 0.0583219 0.0586148 0.995003
example_warp_specialize_gemm_copy_1_gemm_0 0.0176288 0.0177094 0.995448
example_mha_fwd_varlen 0.0329942 0.0331447 0.995458
example_gqa_sink_bwd_bhsd_sliding_window 0.0178338 0.0178992 0.996344
example_warp_specialize_gemm_barrierpipe_stage2 0.0281582 0.0282548 0.99658
example_warp_specialize_gemm_copy_0_gemm_1 0.0267214 0.026785 0.997624
example_fusedmoe_tilelang 0.0967872 0.0969987 0.99782
block_sparse_attn_tilelang 0.00697268 0.00698751 0.997877
example_per_token_cast_to_fp8 0.00662436 0.00663558 0.998309
example_warp_specialize_gemm_softpipe_stage2 0.0176443 0.0176708 0.998498
topk_selector 0.0416608 0.0417234 0.998498
example_tilelang_nsa_fwd 0.00546951 0.00547652 0.99872
example_linear_attn_fwd 0.0280643 0.0280914 0.999034
example_dequant_gemm_w4a8 3.56174 3.56416 0.99932
example_mhc_post 0.106125 0.10619 0.999387
example_group_per_split_token_cast_to_fp8 0.00770348 0.00770683 0.999566
example_gemm 0.0167398 0.0167459 0.999637
example_elementwise_add 0.112905 0.112945 0.999647
example_tilelang_gemm_splitk_vectorize_atomicadd 0.788525 0.788798 0.999654
example_dequant_gemm_bf16_fp4_hopper 0.390943 0.391054 0.999715
example_tilelang_nsa_decode 0.00561493 0.00561624 0.999768
example_mha_sink_fwd_bhsd 0.0127041 0.0127035 1.00005
example_mha_inference 0.0619983 0.0619922 1.0001
example_tilelang_block_sparse_attn 0.0073949 0.00739343 1.0002
example_gemv 0.202586 0.202541 1.00022
example_gqa_fwd_bshd 0.0497286 0.0497125 1.00032
example_gemm_intrinsics 0.0229871 0.0229702 1.00074
example_dequant_gemv_fp16xint4 0.0271149 0.0270935 1.00079
example_mha_fwd_bshd 0.0189009 0.0188847 1.00086
fp8_lighting_indexer 0.0233325 0.0233123 1.00087
sparse_mla_fwd 0.0806824 0.0806118 1.00088
example_tilelang_sparse_gqa_decode_varlen_mask 0.0128955 0.0128836 1.00092
example_gqa_sink_bwd_bhsd 0.0289764 0.0289477 1.00099
example_mha_fwd_bhsd 0.00909485 0.00908223 1.00139
example_mha_sink_fwd_bhsd_sliding_window 0.0124697 0.0124522 1.0014
example_mha_bwd_bshd 0.0290566 0.0290134 1.00149
example_dynamic 0.483545 0.482529 1.00211
example_gqa_decode 0.0411603 0.0410599 1.00245
example_tilelang_sparse_gqa_decode_varlen_indice 0.0117414 0.0117107 1.00262
example_mla_decode 0.320815 0.319869 1.00296
example_convolution 0.764008 0.761738 1.00298
example_tilelang_gemm_splitk 0.771553 0.769098 1.00319
example_gqa_bwd 0.032652 0.0325309 1.00372
example_blocksparse_gemm 0.0132627 0.0131798 1.00629
example_tilelang_gemm_fp8_2xAcc 0.0770575 0.0765476 1.00666
example_tilelang_gemm_fp8 0.232787 0.23123 1.00673
example_mha_sink_bwd_bhsd 0.0507325 0.050223 1.01014
sparse_mla_bwd 0.232304 0.22961 1.01173
example_dequant_gemm_fp4_hopper 0.714361 0.705705 1.01226
example_linear_attn_bwd 0.118427 0.116846 1.01354
example_convolution_autotune 0.747126 0.73418 1.01763
example_gqa_bwd_tma_reduce_varlen 0.033734 0.032957 1.02358

Artifacts

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

@Chennesxu

Copy link
Copy Markdown
Contributor Author

Performance Regression Test Report

Triggered by: @LeiWang1999 Workflow run: https://github.com/tile-ai/tilelang/actions/runs/28357306283

Results

File Original Latency Current Latency Speedup
example_topk 30.0392 39.0637 0.768981
example_mha_sink_bwd_bhsd_sliding_window 0.0379896 0.038633 0.983346
example_dequant_gemm_bf16_mxfp4_hopper 0.355491 0.359938 0.987647
example_vertical_slash_sparse_attn 0.15908 0.160987 0.988158
example_mhc_pre 0.14348 0.144572 0.992446
example_mha_bwd_bhsd 0.0297975 0.0299544 0.994759
sparse_mla_fwd_pipelined 0.0583219 0.0586148 0.995003
example_warp_specialize_gemm_copy_1_gemm_0 0.0176288 0.0177094 0.995448
example_mha_fwd_varlen 0.0329942 0.0331447 0.995458
example_gqa_sink_bwd_bhsd_sliding_window 0.0178338 0.0178992 0.996344
example_warp_specialize_gemm_barrierpipe_stage2 0.0281582 0.0282548 0.99658
example_warp_specialize_gemm_copy_0_gemm_1 0.0267214 0.026785 0.997624
example_fusedmoe_tilelang 0.0967872 0.0969987 0.99782
block_sparse_attn_tilelang 0.00697268 0.00698751 0.997877
example_per_token_cast_to_fp8 0.00662436 0.00663558 0.998309
example_warp_specialize_gemm_softpipe_stage2 0.0176443 0.0176708 0.998498
topk_selector 0.0416608 0.0417234 0.998498
example_tilelang_nsa_fwd 0.00546951 0.00547652 0.99872
example_linear_attn_fwd 0.0280643 0.0280914 0.999034
example_dequant_gemm_w4a8 3.56174 3.56416 0.99932
example_mhc_post 0.106125 0.10619 0.999387
example_group_per_split_token_cast_to_fp8 0.00770348 0.00770683 0.999566
example_gemm 0.0167398 0.0167459 0.999637
example_elementwise_add 0.112905 0.112945 0.999647
example_tilelang_gemm_splitk_vectorize_atomicadd 0.788525 0.788798 0.999654
example_dequant_gemm_bf16_fp4_hopper 0.390943 0.391054 0.999715
example_tilelang_nsa_decode 0.00561493 0.00561624 0.999768
example_mha_sink_fwd_bhsd 0.0127041 0.0127035 1.00005
example_mha_inference 0.0619983 0.0619922 1.0001
example_tilelang_block_sparse_attn 0.0073949 0.00739343 1.0002
example_gemv 0.202586 0.202541 1.00022
example_gqa_fwd_bshd 0.0497286 0.0497125 1.00032
example_gemm_intrinsics 0.0229871 0.0229702 1.00074
example_dequant_gemv_fp16xint4 0.0271149 0.0270935 1.00079
example_mha_fwd_bshd 0.0189009 0.0188847 1.00086
fp8_lighting_indexer 0.0233325 0.0233123 1.00087
sparse_mla_fwd 0.0806824 0.0806118 1.00088
example_tilelang_sparse_gqa_decode_varlen_mask 0.0128955 0.0128836 1.00092
example_gqa_sink_bwd_bhsd 0.0289764 0.0289477 1.00099
example_mha_fwd_bhsd 0.00909485 0.00908223 1.00139
example_mha_sink_fwd_bhsd_sliding_window 0.0124697 0.0124522 1.0014
example_mha_bwd_bshd 0.0290566 0.0290134 1.00149
example_dynamic 0.483545 0.482529 1.00211
example_gqa_decode 0.0411603 0.0410599 1.00245
example_tilelang_sparse_gqa_decode_varlen_indice 0.0117414 0.0117107 1.00262
example_mla_decode 0.320815 0.319869 1.00296
example_convolution 0.764008 0.761738 1.00298
example_tilelang_gemm_splitk 0.771553 0.769098 1.00319
example_gqa_bwd 0.032652 0.0325309 1.00372
example_blocksparse_gemm 0.0132627 0.0131798 1.00629
example_tilelang_gemm_fp8_2xAcc 0.0770575 0.0765476 1.00666
example_tilelang_gemm_fp8 0.232787 0.23123 1.00673
example_mha_sink_bwd_bhsd 0.0507325 0.050223 1.01014
sparse_mla_bwd 0.232304 0.22961 1.01173
example_dequant_gemm_fp4_hopper 0.714361 0.705705 1.01226
example_linear_attn_bwd 0.118427 0.116846 1.01354
example_convolution_autotune 0.747126 0.73418 1.01763
example_gqa_bwd_tma_reduce_varlen 0.033734 0.032957 1.02358

Artifacts

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

The example_topk change in the report looks more like measurement noise than a code regression. This PR only modifies the leftover-iterator check in get_unused_iters, and the new branch (frontier > extent) only affects inputs that reach the over-coverage case from #2394. For kernels that already compiled fine, including example_topk, I would not expect this change to alter the generated code.

I ran a controlled local A/B test using the repository's perf regression entry point (examples/topk/regression_topk_tilelang.py -> example_topk.run_regression_perf()): same machine, both main and this PR rebuilt, GPU idle before each set, 10 runs each with do_bench.

Test environment: NVIDIA TITAN RTX (Turing, sm_75), driver 550.144.03, CUDA 12.4, PyTorch 2.6.0+cu124.

main (10 runs, ms):

[105.4102, 105.0767, 99.6066, 98.5510, 100.3664,
 100.6388, 100.2405, 98.4862, 104.2080, 97.8045]
mean=101.0389  median=100.3034  std=2.6819  spread=7.8%

This PR (10 runs, ms):

[105.1217, 98.3787, 96.5558, 96.9236, 106.1212,
 102.5140, 98.6105, 97.1820, 103.9076, 99.1520]
mean=100.4467  median=98.8812  std=3.4328  spread=9.9%

main / PR = 101.0389 / 100.4467 = 1.006×. The difference between the two means is much smaller than each set's standard deviation, so this looks consistent with normal run-to-run variation, and the ~30% delta from CI (30.04 → 39.06 ms) does not reproduce locally.

@LeiWang1999

Copy link
Copy Markdown
Member

@regression-perf

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown

Performance Regression Test Report

Triggered by: @LeiWang1999
Workflow run: https://github.com/tile-ai/tilelang/actions/runs/28580722167

Results

File Original Latency Current Latency Speedup
example_dequant_gemm_bf16_mxfp4_hopper 0.252902 0.256363 0.9865
example_dequant_gemm_bf16_fp4_hopper 0.266888 0.269095 0.991798
example_mha_sink_fwd_bhsd_sliding_window 0.00972653 0.00979575 0.992934
example_gqa_sink_bwd_bhsd_sliding_window 0.0152035 0.0152966 0.993913
block_sparse_attn_tilelang 0.005437 0.00546117 0.995574
example_mha_bwd_bshd 0.0140178 0.0140768 0.995813
example_mha_fwd_bhsd 0.00697368 0.00700202 0.995952
example_mha_sink_fwd_bhsd 0.00983153 0.00987062 0.99604
example_per_token_cast_to_fp8 0.0042992 0.00431359 0.996662
example_group_per_split_token_cast_to_fp8 0.00560931 0.00562739 0.996787
example_blocksparse_gemm 0.0111392 0.0111751 0.996787
example_mha_sink_bwd_bhsd_sliding_window 0.0262323 0.0263139 0.996902
example_tilelang_block_sparse_attn 0.00523237 0.00524483 0.997623
example_warp_specialize_gemm_barrierpipe_stage2 0.0247712 0.0248254 0.997818
example_warp_specialize_gemm_copy_1_gemm_0 0.0154667 0.0155003 0.997835
example_vertical_slash_sparse_attn 0.13622 0.136505 0.997906
example_gemm_intrinsics 0.0200434 0.0200826 0.998046
example_tilelang_gemm_splitk 0.589905 0.590974 0.998191
example_tilelang_sparse_gqa_decode_varlen_mask 0.0107296 0.010746 0.998473
example_convolution_autotune 0.59072 0.591606 0.998502
sparse_mla_bwd 0.136107 0.136301 0.998575
example_mhc_post 0.065696 0.0657775 0.998761
example_warp_specialize_gemm_copy_0_gemm_1 0.0235379 0.0235651 0.998846
topk_selector 0.0356994 0.0357273 0.999218
example_dequant_gemv_fp16xint4 0.01743 0.0174422 0.999297
example_tilelang_sparse_gqa_decode_varlen_indice 0.00922878 0.00923468 0.999361
example_convolution 0.584601 0.584884 0.999516
example_linear_attn_bwd 0.0967076 0.0967527 0.999533
example_tilelang_gemm_fp8_2xAcc 0.067808 0.0678388 0.999546
example_tilelang_nsa_fwd 0.00406967 0.00407109 0.999653
example_mhc_pre 0.115726 0.115762 0.999689
example_tilelang_nsa_decode 0.0041836 0.00418478 0.999718
example_mha_sink_bwd_bhsd 0.0409783 0.0409842 0.999857
example_dynamic 0.388329 0.38833 0.999998
example_mla_decode 0.284564 0.284539 1.00009
example_gqa_fwd_bshd 0.0297408 0.0297204 1.00069
example_dequant_gemm_fp4_hopper 0.533815 0.533449 1.00069
example_tilelang_gemm_splitk_vectorize_atomicadd 0.584492 0.58403 1.00079
example_warp_specialize_gemm_softpipe_stage2 0.0155385 0.0155219 1.00107
example_gemv 0.149109 0.148925 1.00124
example_fusedmoe_tilelang 0.0764547 0.0763598 1.00124
example_mha_inference 0.0331422 0.0330926 1.0015
example_mha_fwd_varlen 0.0205225 0.0204838 1.00189
example_gqa_decode 0.0305752 0.0305136 1.00202
example_gqa_sink_bwd_bhsd 0.0250859 0.0250315 1.00217
example_gemm 0.0147804 0.0147408 1.00268
example_elementwise_add 0.0694907 0.0693034 1.0027
fp8_lighting_indexer 0.0119847 0.0119461 1.00323
example_mha_fwd_bshd 0.0148078 0.01476 1.00324
example_gqa_bwd 0.0288636 0.0287635 1.00348
example_tilelang_gemm_fp8 0.171259 0.17065 1.00357
example_mha_bwd_bhsd 0.0141165 0.0140513 1.00464
sparse_mla_fwd_pipelined 0.034442 0.0342611 1.00528
sparse_mla_fwd 0.0530204 0.052689 1.00629
example_gqa_bwd_tma_reduce_varlen 0.027962 0.0277871 1.00629
example_linear_attn_fwd 0.0230225 0.0228572 1.00723
example_dequant_gemm_w4a8 2.70035 2.67888 1.00802
example_topk 28.1003 27.752 1.01255

Artifacts

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

@LeiWang1999 LeiWang1999 merged commit b8dc37e into tile-ai:main Jul 3, 2026
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

2 participants