Skip to content

[BugFix] Decode an unsigned sub-word without a zero-point bias - #3348

Open
173787247 wants to merge 1 commit into
tile-ai:mainfrom
173787247:contrib/3301-uint-decode-bias
Open

173787247 wants to merge 1 commit into
tile-ai:mainfrom
173787247:contrib/3301-uint-decode-bias

Conversation

@173787247

@173787247 173787247 commented Sep 30, 2026 •

Copy link
Copy Markdown

Refs #3301 (F163).

Why

_tir_packed_uint_to_uint_to_float masks its field and then subtracts 2^(nbit - 1) - 1, the zero-point of an asymmetric signed decode, even though both the storage and the destination are unsigned:

max_int_value = (1 << (nbit - 1)) - 1
return ((val >> (pos.astype(T.uint32) * tirx.const(nbit, T.uint32))) & tirx.const(
    (1 << nbit) - 1, "uint32")).astype(dtype) - tirx.const(max_int_value, dtype)

Every decoded value comes back shifted by that constant. Decoding the nibbles 0..7 of one uint32:

uint_to_uint decode : [-7.0, -6.0, -5.0, -4.0, -3.0, -2.0, -1.0, 0.0]
expected (unsigned) : [ 0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0, 7.0]
int_to_int control  : [ 0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0, 7.0]

The signed sibling _tir_packed_int_to_int_to_float handles its sign by sign-extending rather than biasing, so the two decoders disagreed on the same payload — the bias does not belong in an unsigned decode.

What changed

Mask and shift, with no bias. A docstring states the contract, since the name and the behaviour are what disagreed.

The helper is not referenced anywhere in the tree, so nothing that currently runs changes behaviour. It lives in a module that four test files import from, and the same function ships under tilelang/quantize/quantization.py in released wheels, so it is worth leaving correct.

Verification

  • Three cases added to testing/python/issue/test_tilelang_u32_signed_decode.py, which already tests a sibling decoder from the same module and whose packing helper and kernel shape are reused here. All three fail on an unmodified tree with Mismatched elements: 4 / 4 (100.0%) and similar; the three existing signed cases pass in both states.
  • test_tilelang_u32_signed_decode.py, test_tilelang_issue_2947.py, test_tilelang_bf16x2_pack_special_values.py and testing/python/quantize/: 39 passed, 1 skipped.
  • The new cases are marked requires_cuda and skip cleanly without a device.
  • The kernel cache was cleared before each run.
  • pre-commit run --files <changed files>: all 12 applicable hooks pass.

Summary

  • Updated _tir_packed_uint_to_uint_to_float to decode packed fields as unsigned values without subtracting a zero-point bias.
  • Added CUDA-parametrized tests for unsigned 2-, 4-, and 8-bit values packed in uint32. The tests check that decoded float32 values match the inputs.

Testing

The author reports 39 passed and 1 skipped across the listed test files and quantization tests. The author also reports that all 12 applicable pre-commit hooks passed. These results were not independently verified.


Verified end to end, together with the other contributions from this batch: VERIFICATION.md

_tir_packed_uint_to_uint_to_float masked its field and then subtracted
2^(nbit - 1) - 1, the zero-point of an asymmetric signed decode, even though
both the storage and the destination are unsigned. Every decoded value came
back shifted by that constant: decoding the nibbles 0..7 of one uint32 gave
[-7, -6, -5, -4, -3, -2, -1, 0] instead of [0, 1, 2, 3, 4, 5, 6, 7], with no
error.

The signed sibling _tir_packed_int_to_int_to_float handles its sign by
sign-extending rather than biasing, so the two decoders disagreed on the same
payload. Mask and shift, with no bias, is the whole unsigned decode.
@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 Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: tile-ai/tilelang/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: b2a8a931-21c7-472d-baab-ccfed4662898

📥 Commits

Reviewing files that changed from the base of the PR and between 994b44e and 93984f5.

📒 Files selected for processing (2)
  • examples/dequantize_gemm/quantize/quantization.py
  • testing/python/issue/test_tilelang_u32_signed_decode.py

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 4 remain after this review.


📝 Walkthrough

Walkthrough

The packed unsigned decoder now returns extracted fields without subtracting an offset. A CUDA-parametrized test checks the decoded values for 2-, 4-, and 8-bit fields.

Changes

Unsigned packed decoding

Layer / File(s) Summary
Unsigned decode and validation
examples/dequantize_gemm/quantize/quantization.py, testing/python/issue/test_tilelang_u32_signed_decode.py
The decoder returns each masked, shifted field cast to the requested dtype without subtracting an offset. The CUDA test packs 2-, 4-, and 8-bit values into uint32 and checks exact float32 equality.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Merge Risk: ⚪ Minimal · up to 93984

The change removes the unintended unsigned decoding offset and adds regression coverage for three field widths. No merge-blocking issue is identified; normal checks should pass before merging.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 6 functions across 2 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: removing the zero-point bias when decoding unsigned sub-word values.
  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


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.

This branch has not been deployed

No deployments
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.

1 participant