Skip to content

Use shapes for MX physical TLoad box; re-enable host-prequant matmul_mx ST - #2620

Merged
YunjiQin merged 1 commit into
hw-native-sys:mainfrom
yanghaoran29:feat/mx-dynamic-scale-offset
Sep 3, 2026
Merged

Use shapes for MX physical TLoad box; re-enable host-prequant matmul_mx ST#2620
YunjiQin merged 1 commit into
hw-native-sys:mainfrom
yanghaoran29:feat/mx-dynamic-scale-offset

Conversation

@yanghaoran29

@yanghaoran29 yanghaoran29 commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Physical MX partition_view / TLoad box sizes now follow fractal-aligned load shapes. A narrower valid_shape only updates tile metadata (e.g. partial M for matmul_mx) and does not shrink the hardware transfer.
  • Keep ConstInt MX offsets and existing EmitMxPhysicalView (no dynamic-offset / cf.assert path in this PR).
  • Remove the module-level skip on tests/st/runtime/ops/test_matmul_mx.py (host-prequant; no quant_mx). Leave test_quant_mx.py / test_quantized_matmul_mx.py skipped under the PTOAS v0.57 pin.
  • Dynamic MX offsets via pass 14 live on private branch feat/mx-pass14-dynamic-offsets for a follow-up PR.

Testing

  • Prior CI run on the un-skipped suite: onboard-tests-a5mx-tests-a5 SUCCESS under PTOAS v0.57
  • CI UT: tests/ut/codegen/test_mx_ops_codegen.py narrowed-valid_shape cases
  • CI: pre-commit / clang-tidy / unit-tests

@coderabbitai

coderabbitai Bot commented Sep 2, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: 5d81e1c7-2316-4105-8779-081cb8dd5fd9

📥 Commits

Reviewing files that changed from the base of the PR and between b2a35bd and f7c9e31.

📒 Files selected for processing (4)
  • src/backend/common/pto_ops_memory.cpp
  • src/ir/op/tile_ops/memory.cpp
  • tests/st/runtime/ops/test_matmul_mx.py
  • tests/ut/codegen/test_mx_ops_codegen.py
💤 Files with no reviewable changes (1)
  • tests/st/runtime/ops/test_matmul_mx.py

Included review availability: Your plan provides up to 2 included reviews per hour; 1 remains after this review.


📝 Walkthrough

Walkthrough

MX-layout tile loads now accept dynamic partition offsets. Static offsets remain validated for minimum and alignment requirements. Backend codegen emits runtime division for dynamic physical offsets. Tests cover generated MLIR and previously skipped MX matmul tests.

Changes

Dynamic MX offsets

Layer / File(s) Summary
Dynamic offset validation
src/ir/op/tile_ops/memory.cpp
MX tile-load validation accepts dynamic block and group offsets. Constant offsets still require valid minimum and alignment constraints.
Runtime physical offset emission and validation
src/backend/common/pto_ops_memory.cpp, tests/ut/codegen/test_mx_ops_codegen.py, tests/st/runtime/ops/test_matmul_mx.py
MX physical views fold constant offsets and emit arith.maxsi plus arith.divui for dynamic offsets. Codegen coverage verifies the generated mx5d_view and pto.tload. MX matmul tests are no longer unconditionally skipped.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: 🟡 Moderate · up to f7c9e

This change allows runtime-provided MX scale offsets to select memory partitions, but invalid or misaligned values could read an unintended or out-of-range scale region. The PR is not merge-ready until that runtime invariant is enforced or explicitly accepted by the owner.

Sequence Diagram(s)

sequenceDiagram
  participant TileLoad
  participant DeduceTileLoadType
  participant EmitMxPhysicalView
  participant MLIR
  TileLoad->>DeduceTileLoadType: provide dynamic block and group offsets
  DeduceTileLoadType-->>TileLoad: accept offsets
  TileLoad->>EmitMxPhysicalView: generate MX physical view
  EmitMxPhysicalView->>MLIR: emit arith.maxsi and arith.divui
  EmitMxPhysicalView->>MLIR: emit mx5d_view and pto.tload
Loading

Poem

A rabbit hops through tiles of light
Dynamic offsets divide just right
Constants fold without a fuss
MX views bloom in MLIR for us
Tests wake up and greet the night

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 8 functions across 3 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.
Title check ✅ Passed The title identifies two real changes: MX physical TLoad shape handling and re-enabling host-prequantized matmul_mx system tests. It omits dynamic MX offset support but remains related and sufficientl…
Description check ✅ Passed The description covers MX physical views, MX offsets, test re-enablement, and testing status. It conflicts with the changeset by stating that dynamic-offset support is excluded, although the code and …
Full details: Title check

Explanation

The title identifies two real changes: MX physical TLoad shape handling and re-enabling host-prequantized matmul_mx system tests. It omits dynamic MX offset support but remains related and sufficiently specific.

Full details: Description check

Explanation

The description covers MX physical views, MX offsets, test re-enablement, and testing status. It conflicts with the changeset by stating that dynamic-offset support is excluded, although the code and objectives include dynamic offsets.

  • Fix all pre-merge checks with AI

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.

@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: f7c9e31908

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/backend/common/pto_ops_memory.cpp Outdated
@yanghaoran29
yanghaoran29 force-pushed the feat/mx-dynamic-scale-offset branch from f7c9e31 to 015a412 Compare September 2, 2026 06:49

@YunjiQin YunjiQin left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Review — dynamic MX scale load offsets

Reviewed at 015a412. Two independent changes are bundled here. The valid_shapeshapes half is correct and I would take it as-is; the dynamic-offset half cannot work on PTOAS as written, and the mechanism it needs already exists in the tree.

Blocking — cf.assert is not emittable by PTOAS, at any version

EmitMxPhysicalOffsetCode emits the first cf dialect op in the codebase — a scan of src/codegen + src/backend shows we only ever emit pto / arith / scf / func. PTOAS parses it (so the dialect is registered) but the C++ emitter has no printer for it:

$ ptoas --pto-arch=a5 --pto-level=level3 -o /dev/null mx_assert.mlir
loc("mx_assert.mlir":17:3): error: 'cf.assert' op unable to find printer for op
Error: Failed to emit C++.
$ echo $?
1

The same file with only the cf.assert line deleted compiles clean (exit 0). I swept every installed version — 0.54, 0.55, 0.57, 0.58, 0.59, 0.60 all fail identically. This is not the v0.57 pin: PTOAS's EmitC backend has never had a printer for cf.assert. Reproduce with /usr/local/ptoas/<ver>/bin/ptoas; I can share the two .mlir files.

Upstream cf.assert is a runtime assertion (ConvertControlFlowToLLVM lowers it to cond_br + abort), but PTOAS never takes that path, so the intended semantics can't be reached.

This collapses the PR's premise. The summary says codegen emits remui + cf.assert + divui "so unaligned runtime offsets cannot silently floor to the wrong scale block". Remove the unemittable op and remui/cmpi have no consumer; what remains is maxsi + divui — precisely the silent-floor behaviour the PR set out to prevent. There is no degraded-but-useful subset here.

One question on the checklist: Local A5 … matmul_mx path PASS (aligned dynamic offsets). Since IR containing the assert cannot compile, were those offsets constant-folded to ConstInt by Simplify before codegen — i.e. did that run take the ci->value_ / divisor branch and never exercise the new path?

The mechanism we need is already in the tree

include/pypto/ir/transforms/utils/tensor_view_semantics.h solves this exact problem at compile time, for NZ:

  • IsProvableMultipleOf(expr, divisor, facts, budget) — proves a symbolic offset is a multiple of the fractal factor (constants, loop vars whose start and step are both multiples, and sums/differences/constant multiples of those), walking the SSA def chain under a visit budget so the pass stays O(N).
  • IsProvableNonNegative(...) — its doc comment describes our failure mode verbatim: "FloorDiv(n0, 16) is -1; codegen then clamps a negative pto.partition_view offset to 0 rather than failing, so the load silently reads fractal 0 instead of reporting anything. That is the same silent-wrong-data shape #2543 fixed for row indices."
  • BlockNzOffsets — maps logical […, r0, c0off] into blocked […, c0off/c0, r0/16, 0, 0], folding constants with a user-facing CHECK_SPAN and dividing the result (MakeFloorDiv) rather than re-associating the arithmetic, with the wraparound-soundness argument written out.

Structurally identical to MX_A_ZZ [M, G] → [1, M/16, G/2, 16, 2]. All compile-time, zero runtime cost, no PTOAS feature required, and failures are user-level errors that name the provable forms.

The consumer is BlockNzTensorViews (pass 14), currently gated on TensorLayout::NZ. Its header states why the blocking lives in a pass rather than the backend:

After this pass no logical-shaped NZ TensorType survives, so nothing downstream — including codegen, which derives every rank from TensorType::shape_ — needs to know NZ is special. That is the reason the blocking lives here rather than in the backend: EmitMakeTensorViews, GetTensorViewTypeString and the tile.load partition_view emitter each read the rank independently and must agree.

That is the argument against EmitMxPhysicalView, which hand-builds the rank-5 view and consequently has to hand-build the partition type and bypass the generic path ("skip the generic parameter view — see EmitMakeTensorViews").

For contrast, here is an NZ load with a symbolic loop-var offset today:

%x_view = pto.make_tensor_view %arg0, shape = [%c4_index, %c8_index, %c16_index, %c16_index], ... {layout = #pto.layout<nz>}
scf.for %k__idx_v0 = %c0_index to %c64_index step %c16_index {
  %0 = arith.divsi %k__idx_v0, %c16_index : index   // pass 14's MakeFloorDiv, generic scalar codegen
  %1 = arith.maxsi %0, %c0_index : index            // existing GetIndexOffsetCodes UB guard
  %x_pview = pto.partition_view %x_view, offsets = [%c0_index, %1, %c0_index, %c0_index], ...

A generic view, two arith ops, no runtime check — alignment and non-negativity were proven in the pass. And MX fits pass 14's stated Milestone 1 scope point for point: read-only, target_memory=Mat (which MX already mandates), whole-byte dtype, static shapes, fractal-aligned shapes. Only the factors differ (16×2 vs 16×c0). Moving MX there is not a rewrite so much as putting it where NZ already lives.

Note the proof needs NzOffsetFacts (definition map, loop start/step, SPMD non-negativity), which only a pass can supply — a default-constructed one "proves nothing beyond constant folding". So DeduceTileLoadType is not a viable home; it would reject every dynamic offset.

Independently correct — worth splitting out

The valid_shapeshapes change stands on its own and does not depend on any of the above. It is also a robustness improvement: partition_dims feeds the static type string !pto.partition_tensor_view<1x1x1x16x2x…>, so sourcing it from shapes (required static) rather than valid_shape (may be dynamic) is strictly safer. Existing callers that omit valid_shape get shapes, so there is no behaviour change.

Suggest landing that as its own PR — it need not wait on the offset redesign.

test_matmul_mx.py un-skip

The skip reason shared by all three MX STs is "MX pack/layout requires PTOAS v0.60". test_matmul_mx.py builds MX_A_ZZ itself via _pack_a_scale, so "host-prequant, no quant_mx" does not address that blocker — quant_mx is one way to produce the pack, not the pack itself. Is the CI pin back at v0.60? If it is still v0.57, mx-tests-a5 will fail. The checklist item is unticked, so this looks unverified either way.

Non-blocking

  • DRY. The first half of EmitMxPhysicalOffsetCode (ConstInt branch, EmitCastToIndex, arith.maxsi clamp) duplicates GetIndexOffsetCodes (pto_ops_shared.cpp:117-139) line for line — and that helper is already in this file's using list.
  • Redundant check. The new valid_shape <= shapes loop overlaps InferWindowReadValidShape (type_inference.cpp:665-670), which already enforces this for every tile.load with a span and a better message. Because the new loop runs first, it shadows that message for MX. It also uses CHECK where args[3]->span_ is in hand — error-checking.md asks for the _SPAN form. The only genuinely new predicate is valid > 0, which applies to all loads and would be better placed in the generic path.
  • Error class. check_aligned_if_static only catches literals. pl.load(a_s, [i * 8, 0], ...) folded to 8 by Simplify reaches the codegen INTERNAL_CHECK_SPAN, so a user error surfaces as "Internal error". The proof-based approach removes this by construction.
  • Wording. check_static_aligned(shapes_tuple->..., "load block size") now describes shapes, not valid_shape; "load block shape" would avoid the ambiguity.

Test coverage

  • assert "%c16_index" in mlir in test_mx_scale_load_accepts_narrowed_valid_shape does not test what its comment claims. %c16_index appears in alloc_tile, in the make_tensor_view shape, and in the partition_view sizes — the assertion holds even if the partition box shrinks. Assert on the pto.partition_view line's sizes = [%c1_index, %c1_index, %c1_index, %c16_index, %c2_index] instead. Same for %c16_index / %c2_index in the dynamic-offset test: both are shape constants, so only the remui / cf.assert / divui assertions carry information.
  • The new valid_shape <= shapes check has zero coverage — it fires only when both sides are ConstInt, and the test passes a dynamic valid_rows. The PR describes static partial-M narrowing; a valid_shape=[8, 2] case would exercise both the description and the check, plus a valid_shape=[32, 2] negative.
  • No negative test that a static misaligned offset is still rejected ([8, 0] / [0, 1]). That is the boundary the check_static_alignedcheck_aligned_if_static swap is most likely to break, and nothing in tests/ covers it today.
  • Neither new test reaches matmul_mx, though the comment justifies narrowed valid_shape by what matmul_mx consumes. "TLoad moves 16 rows / tile declares valid_row 8" is the combination that needs on-device evidence.
  • tests/ut/codegen/test_mx_ops_codegen.py has no trailing pytest.main block (pre-existing; worth fixing while here).

Suggested split

Scope State
A valid_shapeshapes + test fixes Landable once the non-blocking items above are addressed
B Dynamic MX offsets via pass 14 + the proof helpers Needs redesign
C test_matmul_mx.py un-skip Pending the PTOAS pin answer

That way A's benefit isn't held up by B, and B can land where it architecturally belongs.

@yanghaoran29
yanghaoran29 force-pushed the feat/mx-dynamic-scale-offset branch 2 times, most recently from 9352692 to 17b4e32 Compare September 2, 2026 09:55
…mx ST.

Physical partition sizes now follow fractal-aligned load shapes so a
narrowed valid_shape only affects tile metadata. Keep ConstInt MX offsets
and EmitMxPhysicalView. Drop the module-level skip on test_matmul_mx
(PTOAS v0.57 is enough for host-prequant); leave quant_mx suites skipped.
@yanghaoran29
yanghaoran29 force-pushed the feat/mx-dynamic-scale-offset branch from 17b4e32 to def1a59 Compare September 3, 2026 02:10
@yanghaoran29 yanghaoran29 changed the title Allow dynamic MX scale load offsets; re-enable host-prequant matmul_mx ST Use shapes for MX physical TLoad box; re-enable host-prequant matmul_mx ST Sep 3, 2026
@yanghaoran29

Copy link
Copy Markdown
Contributor Author

@YunjiQin Thanks again — following your Suggested split, this PR is now A + C only.

Scope after force-push (def1a598)

  • A: EmitMxPhysicalView / DeduceTileLoadType take fractal alignment from load shapes; narrowed valid_shape stays tile metadata. ConstInt offsets + EmitMxPhysicalView unchanged (no remui / cf.assert).
  • C: test_matmul_mx.py module skip removed; quant suites stay skipped.
  • B (dynamic MX offsets / pass 14 / FloorDiv proofs / dropping EmitMx*): saved on private branch feat/mx-pass14-dynamic-offsets for a follow-up PR.

Blocking (cf.assert)

Agreed — that path is not in this tip.

C / PTOAS pin

Still v0.57 (toolchain/versions.env). On the previous tip that already un-skipped test_matmul_mx, CI onboard-tests-a5mx-tests-a5 SUCCESS, so host-prequant matmul_mx does not need the v0.60 pack/quant_mx path the shared skip reason assumed.

Non-blocking from your review

  • Wording: load block/group shape
  • No redundant valid_shape <= shapes loop (rely on InferWindowReadValidShape)
  • UT asserts partition_view sizes = [%c1_index, %c1_index, %c1_index, %c16_index, %c2_index] for both static [8,2] and dynamic valid_rows narrowing

Happy to iterate on A/C nits here; B will come separately.

@YunjiQin
YunjiQin merged commit c2414da into hw-native-sys:main Sep 3, 2026
20 checks passed
yanghaoran29 added a commit to yanghaoran29/pypto that referenced this pull request Sep 3, 2026
Extend pass 14 to block MX_A_ZZ/MX_B_NN into packed rank-5 and rewrite
tile.load windows with IsProvableMultipleOf/NonNegative (including FloorDiv
forms like ks=k0//32). Drop EmitMxPhysicalView so codegen uses the generic
make_tensor_view path; unprovable offsets fail at compile time. Builds on
hw-native-sys#2620 (shapes-sized physical TLoad box / narrowed valid_shape metadata).
yanghaoran29 added a commit to yanghaoran29/pypto that referenced this pull request Sep 3, 2026
Extend pass 14 to block MX_A_ZZ/MX_B_NN into packed rank-5 and rewrite
tile.load windows with IsProvableMultipleOf/NonNegative (including FloorDiv
forms like ks=k0//32). Use per-phase function stamps for idempotence, preserve
ND/MX backing aliases and Submit return types, and validate canonical physical
load geometry.

Drop EmitMxPhysicalView so codegen uses the generic make_tensor_view path;
unprovable offsets fail at compile time and narrowed valid shapes remain tile
metadata. Builds on hw-native-sys#2620 (shapes-sized physical TLoad box / narrowed
valid_shape metadata).
yanghaoran29 added a commit to yanghaoran29/pypto that referenced this pull request Sep 3, 2026
Extend pass 14 to block MX_A_ZZ/MX_B_NN into packed rank-5 and rewrite
tile.load windows with IsProvableMultipleOf/NonNegative (including FloorDiv
forms like ks=k0//32). Use per-phase function stamps for idempotence, preserve
ND/MX backing aliases and Submit return types, and validate canonical physical
load geometry.

Drop EmitMxPhysicalView so codegen uses the generic make_tensor_view path;
unprovable offsets fail at compile time and narrowed valid shapes remain tile
metadata. Builds on hw-native-sys#2620 (shapes-sized physical TLoad box / narrowed
valid_shape metadata).
yanghaoran29 added a commit to yanghaoran29/pypto that referenced this pull request Sep 3, 2026
Extend pass 14 to block MX_A_ZZ/MX_B_NN into packed rank-5 and rewrite
tile.load windows with IsProvableMultipleOf/NonNegative (including FloorDiv
forms like ks=k0//32). Follow alignment facts through outlined scalar
parameters, require every call site to prove them, preserve ND/MX backing
aliases and Submit return types, and validate canonical physical load geometry.

Drop EmitMxPhysicalView so codegen uses the generic make_tensor_view path;
unprovable offsets fail at compile time and narrowed valid shapes remain tile
metadata. Builds on hw-native-sys#2620 (shapes-sized physical TLoad box / narrowed
valid_shape metadata).
yanghaoran29 added a commit to yanghaoran29/pypto that referenced this pull request Sep 3, 2026
Extend pass 14 to block MX_A_ZZ/MX_B_NN into packed rank-5 and rewrite
tile.load windows with IsProvableMultipleOf/NonNegative (including FloorDiv
forms like ks=k0//32). Follow alignment facts through outlined scalar
parameters, require every call site to prove them, preserve ND/MX backing
aliases and Submit return types, and validate canonical physical load geometry.

Drop EmitMxPhysicalView so codegen uses the generic make_tensor_view path;
unprovable offsets fail at compile time and narrowed valid shapes remain tile
metadata. Builds on hw-native-sys#2620 (shapes-sized physical TLoad box / narrowed
valid_shape metadata).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

2 participants