Skip to content

[Frontend] DMA transfer op + aligned-only decompose pass for >4D access - #257

Merged
YWHyuk merged 4 commits into
feature/mlir-python-bindingsfrom
dma-transfer/codegen
Jun 16, 2026
Merged

[Frontend] DMA transfer op + aligned-only decompose pass for >4D access#257
YWHyuk merged 4 commits into
feature/mlir-python-bindingsfrom
dma-transfer/codegen

Conversation

@YWHyuk

@YWHyuk YWHyuk commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

Stacked on top of #256 (feature/mlir-python-bindings). Targets that branch, not develop.

What

Represents a DMA whose access exceeds the 4D Gemmini descriptor limit as a high-level togsim.transfer op, and decomposes it into <=4D memref.dma_start in a Python out-of-line MLIR pass. Removes the hard-fail at >4D in get_dma_info.

Pieces

  • Emit: get_dma_info no longer raises at >4D; it builds the full N-D tile and emits togsim.transfer (emit_transfer) carrying the access (dram_stride, tile_stride), vlane info, and the SSA operands a dma_start needs. init_tile_size generalized to any rank (logical vs physical tile separation, no rank cap).
  • Decompose pass (passes/decompose_transfer.py): aligned-only mechanical peel.
    • Unit-dim collapse (validated end-to-end): drop extent-1 tile dims; collapse the SRAM spad to the effective rank via memref.collapse_shape; remap vlane_split_axis to the collapsed-dim index. DRAM stays flat rank-1 with its N-D structure in dram_stride.
    • Genuine >4 effective rank (isolation-validated): peel the outer dims by unrolled memref.subview slices. Currently unreachable through the pipeline (init_tile_size caps non-unit tile dims at 3), implemented for completeness.
  • lower_to_llvm.py: add expand-strided-metadata to lower collapse_shape/subview.

Scope decision

The pass is a pure mechanical rank peel of already-affine access. It does NOT linearize floor/mod and does NOT relayout; those move upstream (aligned floor/mod -> axis split at the scheduling layer; misaligned -> graph copy insertion). The pass asserts on non-affine residue rather than silently relaying out. See docs/dma-transfer-lowering.md.

Validation

End-to-end (Gem5 + Spike + TOGSim, allclose=True) on 5D permute, 5D broadcast, 5D reduction. No regression on 2D/3D/elementwise add. The >4-effective peel validated in isolation via lower_text on a synthetic transfer.

🤖 Generated with Claude Code

YWHyuk and others added 4 commits June 16, 2026 13:27
Phase 1 (emission only) of the DMA transfer-op plan. A DMA access whose
logical tile exceeds the 4D Gemmini descriptor limit no longer hard-fails;
it emits a high-level togsim.transfer op for a later decompose pass to peel
into a loop of <=4D memref.dma_start. The decompose pass is deferred.

init_tile_size in mlir_common.py is generalized to any rank by separating
the logical tile from the physical (<=4D) descriptor: only the innermost
dims carry the vectorized tile, all further-outer dims stay 1, no rank cap.
The nr_dim>=3 formula reproduces the old 3D/4D values exactly, removing the
"dummy tile size fail!" assertion that conflated logical and physical rank.

mlir_codegen_backend.py: get_dma_info >4D branch builds the full N-D tile
and sets _dma_needs_transfer; load()/store() emit togsim.transfer when the
flag is set, otherwise the existing get_dma_code path is unchanged so
aligned <=4D DMAs stay bit-identical.

docs/dma-transfer-lowering.md: append the alignment-decomposability theory
(aligned vs misaligned, modular valid iff y*z|extent, mixed-radix), the
one-loop-axis -> several-implicit-axes generalization for complex fusion,
a case-handling summary table, and the Phase 1 implementation status.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Scope decision: the decompose pass is a pure mechanical rank peel of an
already-affine access. It no longer linearizes floor/mod and no longer does
relayout. Those move upstream: aligned floor/mod is removed by axis splitting
at the Inductor scheduling layer (axis-split-scheduling.md), misaligned access
is resolved by graph-level copy insertion.

The pass asserts (fail loud) on any non-affine residue instead of silently
inserting a relayout, which would be a hidden perf cliff and a global layout
decision made at the wrong layer. Adds a division-of-labor table and updates
the contract, cost, placement, migration, risks, and deferred-work sections.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… path)

Add passes/decompose_transfer.py: a Python out-of-line MLIR pass that lowers
each togsim.transfer to a customized memref.dma_start, the aligned-only
mechanical peel from the design doc. This first increment handles the
unit-dim-collapse case (descriptor reaches <=4D once extent-1 tile dims are
dropped); genuine >4 effective rank still raises NotImplementedError pending
the affine.for peel loop.

Mechanics:
- Drop extent-1 tile dims. Collapse the SRAM spad memref to the effective rank
  via memref.collapse_shape (the customized dma_start convention requires SRAM
  rank == #indices == len(sram_stride)); DRAM stays flat rank-1 with its N-D
  structure in dram_stride.
- Remap vlane_split_axis from the original tile-dim index to the collapsed-dim
  index and rematerialize the const.

Supporting changes:
- emit_transfer carries the SSA operands a dma_start needs (dma_type,
  vlane_stride) and the vlane_split_axis value as an attr (so the pass can
  remap it); operand prep mirrors get_dma_code for cache compatibility.
- lower_to_llvm.py adds expand-strided-metadata to lower collapse_shape.
- register decompose_transfer before lower_vlane_idx in passes/__init__.py.

Validated end-to-end (Gem5 + Spike + TOGSim, allclose=True) on the 5D permute
x.permute(4,3,2,1,0).contiguous() + 1.0; no regression on 2D/3D/elementwise add.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…bview

Make the decompose pass total: when more than 4 non-unit tile dims survive,
keep the inner 4 as the <=4D descriptor and peel the outer dims by full
unrolling -- one customized memref.dma_start per outer-index combo, the SRAM
slice a rank-reduced memref.subview at the static slice offset, the DRAM base
dram_idx + constant. Unrolling keeps the slice offsets static so no
per-iteration SRAM index arithmetic is needed; the vlane axis is remapped into
the inner descriptor.

Currently unreachable through the full pipeline: init_tile_size caps non-unit
tile dims at 3 (effective rank <= 3 in practice), so this path is implemented
for completeness / future tilings and validated only in isolation via
lower_text on a synthetic 5-effective transfer (2 descriptors, correct subview
offsets 0/24, dram offset +1, inner strides). The unit-collapse fast path and
2D/3D/5D pipeline runs are unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@YWHyuk
YWHyuk merged commit 0ebe0d1 into feature/mlir-python-bindings Jun 16, 2026
@YWHyuk
YWHyuk deleted the dma-transfer/codegen branch June 16, 2026 07:02
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