Skip to content

dialects: (linalg) support tiling of linalg.generic ops over tensors - #6362

Draft
dipo101 wants to merge 2 commits into
xdslproject:mainfrom
dipo101:linalg-tiling-tensor
Draft

dialects: (linalg) support tiling of linalg.generic ops over tensors#6362
dipo101 wants to merge 2 commits into
xdslproject:mainfrom
dipo101:linalg-tiling-tensor

Conversation

@dipo101

@dipo101 dipo101 commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Part of #6140, which tracks Linalg tiling support missing relative to upstream MLIR. This covers the tensor-based tiling checkbox.

Tiling a linalg.generic over memrefs slices each operand with a memref.subview and writes results through that view. Tensors have value semantics, so a tile is a new value that has to be written back explicitly and carried out of the loops it was computed in. That difference is what this adds.

For an op over tensors, tiling now produces:

%C = scf.for %5 = %0 to %1 step %3 iter_args(%6 = %B) -> (tensor<4x4xf32>) {
  %7 = scf.for %8 = %0 to %2 step %4 iter_args(%9 = %6) -> (tensor<4x4xf32>) {
    %10 = tensor.extract_slice %A[%5, %8] [2, 2] [1, 1] ...
    %11 = tensor.extract_slice %9[%5, %8] [2, 2] [1, 1] ...
    %12 = linalg.generic ins(%10) outs(%11) ... -> tensor<2x2xf32>
    %13 = tensor.insert_slice %12 into %9[%5, %8] [2, 2] [1, 1] ...
    scf.yield %13 : tensor<4x4xf32>
  }
  scf.yield %7 : tensor<4x4xf32>
}

The outermost loop carries the original output tensor and each nested loop carries the enclosing loop's block argument, so the output tile is extracted from the value accumulated so far rather than from the original. The outermost loop's results replace the results of the original op.

Only scf.for is generated. xDSL has no scf.forall, so the parallel form upstream also supports is out of scope here.

Commits

  1. dialects: (tensor) add mixed static/dynamic builders to extract_slicetensor.extract_slice could only be built with fully static offsets, sizes and strides, so it could not express a slice taken at a loop induction variable. Adds get() and infer_result_type(), mirroring the existing memref.SubviewOp pair.
  2. dialects: (linalg) thread loop-carried values through tile loops — gives _build_tile_loops an optional iter_args parameter.
  3. dialects: (linalg) slice tensor operands with tensor.extract_slice — widens the operand type and picks the slicing op based on it.
  4. dialects: (linalg) write tiles back into the tensors carried by tile loops — adds the tensor.insert_slice write-back and the operand-carrying yields.
  5. dialects: (linalg) tile linalg.generic ops over tensors — stops rejecting tensor operands during analysis.

Commits 2 to 4 are each inert on their own, since tensor operands are still rejected until the last one. They are kept separate because each is a distinct change to explain.

Behaviour that is unchanged

Tiling memrefs carries nothing, writes nothing back and yields nothing, so its generated IR is byte-identical to before. The existing filecheck expectations are untouched.

An op that reads a tensor and writes a memref has no results, so nothing is carried and the output is still written through its subview. That case used to be rejected and now tiles; it has a test.

Newly rejected

An op with both memref and tensor outputs is rejected. Such an op verifies today but has fewer results than outputs, and the two kinds of output would have to be written back differently within one loop nest.

Tests

  • filecheck: both dimensions tiled, one dimension tiled, and tensor input with memref output
  • filecheck rejection: mixed memref and tensor outputs
  • unit: the loop-carried threading, the slicing op chosen per operand type, the new rejections, and the extract_slice builders

Open questions for review

  • Is rejecting mixed memref-and-tensor outputs the right call, or should that be supported?
  • tensor.ExtractSliceOp has no verify_ checking that the dynamic index markers match the operand count, unlike memref.SubviewOp. This is pre-existing and deliberately left out of this PR, but happy to add it here if preferred.
  • ExtractSliceOp.infer_result_type takes no reduce_rank flag, unlike the memref one, since nothing needs it yet.
  • The emitted IR has not been diffed against mlir-opt output, as the MLIR-conversion tests are opt-in.

@codecov

codecov Bot commented Aug 13, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.95%. Comparing base (68e9b73) to head (fce5d67).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #6362   +/-   ##
=======================================
  Coverage   86.94%   86.95%           
=======================================
  Files         440      440           
  Lines       65983    65999   +16     
  Branches     7493     7493           
=======================================
+ Hits        57368    57387   +19     
+ Misses       7037     7036    -1     
+ Partials     1578     1576    -2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@superlopuh

Copy link
Copy Markdown
Member

This is great! This PR is too large to review IMO, and you've already broken it down nicely, so let's iterate in smaller PRs. I'll mark this PR as draft, can you please open a new one with just the first commit to start with?

@superlopuh
superlopuh marked this pull request as draft August 14, 2026 09:05
@superlopuh
superlopuh self-requested a review August 14, 2026 09:05
@superlopuh superlopuh added the dialects Changes on the dialects label Aug 14, 2026
@dipo101

dipo101 commented Aug 14, 2026

Copy link
Copy Markdown
Contributor Author

Thanks! Opened #6366 with just the first commit (the tensor.extract_slice builders). I'll keep this PR as the draft overview and open the following ones as each lands.

…loops

A tile of a memref is written through the subview that views it, but a tile
of a tensor is a new value, so it has to be written back explicitly and
carried out of the loops it was computed in.

Pass the outputs of an op with tensor semantics as the loop nest iteration
arguments, slice those outputs from the values the innermost loop carries
rather than from the originals, and write each computed tile back with a
`tensor.insert_slice`. The innermost loop yields the updated tensors and
each enclosing loop yields the results of the loop nested inside it, so the
outermost loop produces the fully updated tensors, which then replace the
results of the original op.

Slicing an output from the original tensor rather than from the carried
value, or erasing the op rather than replacing its results, would both give
IR that verifies but drops the work of the surrounding iterations.

The offsets, sizes and strides of a tile are now computed once as
`SliceParameters`, since a tile has to be written back exactly where it was
extracted from.

Tiling memrefs carries nothing, writes nothing back, and yields nothing, so
its generated IR is unchanged. Tiling tensors is still rejected during
analysis, so this is not yet reachable from the pass.
Everything needed to tile an op with tensor operands is now in place, so
stop rejecting them during analysis.

The operand check becomes a positive one: memrefs and tensors are both
tileable, and anything else is still rejected. Operands with a mix of memref
and tensor outputs are rejected instead, since those would have to be
written back in different ways within one loop nest, and the pass would
otherwise fail while pairing the results of the tiled op with the values the
loops carry.

An op whose outputs are memrefs is unaffected, including one that reads a
tensor and writes a memref: it has no results, so nothing is carried and the
output is still written through its subview.
@dipo101
dipo101 force-pushed the linalg-tiling-tensor branch from 955281e to fce5d67 Compare August 17, 2026 09:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dialects Changes on the dialects

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants