-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[mlir][linalg] Refactor vectorization hooks to improve code reuse #141244
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
[mlir][linalg] Refactor vectorization hooks to improve code reuse #141244
Conversation
@llvm/pr-subscribers-mlir-vector Author: Andrzej Warzyński (banach-space) ChangesThis patch refactors two vectorization hooks in Vectorization.cpp:
CONTEXTThis is effectively a refactoring of the logic for vectorizing
At the time, reuse of the shared CHANGES IN
|
@llvm/pr-subscribers-mlir-linalg Author: Andrzej Warzyński (banach-space) ChangesThis patch refactors two vectorization hooks in Vectorization.cpp:
CONTEXTThis is effectively a refactoring of the logic for vectorizing
At the time, reuse of the shared CHANGES IN
|
@llvm/pr-subscribers-mlir-llvm Author: Andrzej Warzyński (banach-space) ChangesThis patch refactors two vectorization hooks in Vectorization.cpp:
CONTEXTThis is effectively a refactoring of the logic for vectorizing
At the time, reuse of the shared CHANGES IN
|
@llvm/pr-subscribers-mlir Author: Andrzej Warzyński (banach-space) ChangesThis patch refactors two vectorization hooks in Vectorization.cpp:
CONTEXTThis is effectively a refactoring of the logic for vectorizing
At the time, reuse of the shared CHANGES IN
|
eccff09
to
f0922e9
Compare
This patch removes `inputVecSizesForLeadingDims` from the parameter list of `createWriteOrMaskedWrite`. That argument is unnecessary — vector sizes can be obtained from the `vecToStore` parameter. Since this doesn't change behavior or test results, it's marked as NFC. Additional cleanups: * Renamed `vectorToStore` to `vecToStore` for consistency and brevity. * Rewrote a conditional at the end of the function to use early exit, improving readability: ```cpp // BEFORE: if (maskingRequried) { Value maskForWrite = ...; write = maskOperation(write, maskForWrite); } return write; // AFTER if (!maskingRequried) return write; Value maskFroWrite = ...; return vector::maskOperation(builder, write, maskForWrite); ``` This change addresses a TODO from #141244.
This patch removes `inputVecSizesForLeadingDims` from the parameter list of `createWriteOrMaskedWrite`. That argument is unnecessary — vector sizes can be obtained from the `vecToStore` parameter. Since this doesn't change behavior or test results, it's marked as NFC. Additional cleanups: * Renamed `vectorToStore` to `vecToStore` for consistency and brevity. * Rewrote a conditional at the end of the function to use early exit, improving readability: ```cpp // BEFORE: if (maskingRequried) { Value maskForWrite = ...; write = maskOperation(write, maskForWrite); } return write; // AFTER if (!maskingRequried) return write; Value maskFroWrite = ...; return vector::maskOperation(builder, write, maskForWrite); ``` This change addresses a TODO from #141244.
f0922e9
to
82cc2fe
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall looks good to me, but there are some cases that used to use in_bounds, and now use masking, which I'm not 100% sure about. Maybe it would be easier to split that part out into a second PR, so it can be reviewed separately by others with more context?
Hey @Max191 , thanks for taking a lot and for your insightful comments - that's much appreciated 🙏🏻 I totally agree with your suggestions and have made the changes accordingly. As you will notice, all the changes in the following test files have been reverted:
That's expected - these are the test files in which we do not specify the vector sizes, hence there should be no masking. Re future steps:
Same. I intend to investigate this in the near future and might propose some changes. But I totally agree that this is quite nuanced and that we should approach this in small, incremental steps. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, this LGTM now! Thanks for addressing all the comments
This patch refactors two vectorization hooks in Vectorization.cpp: * `createWriteOrMaskedWrite` gains a new parameter for write indices, aligning it with its counterpart `createReadOrMaskedRead`. * `vectorizeAsInsertSliceOp` is updated to reuse both of the above hooks, rather than re-implementing similar logic. CONTEXT ------- This is effectively a refactoring of the logic for vectorizing `tensor.insert_slice`. Recent updates added masking support: * #122927 * #123031 At the time, reuse of the shared `create*` hooks wasn't feasible due to missing parameters and overly rigid assumptions. This patch resolves that and moves us closer to a more maintainable structure. CHANGES IN `vectorizeAsInsertSliceOp` ------------------------------------- * Introduces a clear distinction between the destination tensor and the vector to store, via named variables like `destType`/`vecToStoreType`, `destShape`/`vecToStoreShape`, etc. * Ensures the correct rank and shape are used for attributes like in_bounds. For example, the size of the in_bounds array now matches the source vector rank, not the tensor rank. * Drops the assumption that `vecToStoreRank == destRank` — this doesn't hold in many real examples. * Deduces mask dimensions from `vecToStoreShape` (vector) instead of `destShape` (tensor). (Eventually we should not require `inputVecSizesForLeadingDims` at all — mask shape should be inferred.) NEW HELPER: `isMaskTriviallyFoldable` ------------------------------------- Adds a utility to detect when masking is unnecessary. This avoids inserting redundant masks and reduces the burden on canonicalization to clean them up later. Example where masking is provably unnecessary: ```mlir %2 = vector.mask %1 { vector.transfer_write %0, %arg1[%c0, %c0, %c0, %c0, %c0, %c0] {in_bounds = [true, true, true]} : vector<1x2x3xf32>, tensor<9x8x7x1x2x3xf32> } : vector<1x2x3xi1> -> tensor<9x8x7x1x2x3xf32> ``` Also, without this hook, tests are more complicated and require more matching. TEST CHANGES ----------- This patch primarily affects vectorization of: * `tensor.insert_slice`, now refactored to use shared hooks. `tensor.pad` vectorization patterns, which internally use `tensor.insert_slice`, are also _effectively_ updated. Note, only pad-with-patterns.mlir is affected. Most test updates involve the insertion of masks that were previously missing — this reflects a correctness fix, not a regression. In all cases, the added masks are indeed required. You’ll also notice more repeated constants (`arith.constant 0 : index`), due to increased use of helper hooks. This will be cleaned up separately via a constant cache (see #138265 for discussion). NOTE FOR REVIEWERS ------------------ This is a fairly substantial rewrite. You may find it easier to review `createWriteOrMaskedWrite` as a new method rather than diffing line-by-line. TODOs (future PRs) ------------------ Further alignment of `createWriteOrMaskedWrite` and `createReadOrMaskedRead`: * Move `createWriteOrMaskedWrite` next to `createReadOrMaskedRead` (in VectorUtils.cpp) * Make `createReadOrMaskedRead` leverage `isMaskTriviallyFoldable`. * Extend `isMaskTriviallyFoldable` with value-bounds-analysis. See the updated test in transform-vector.mlir for an example that would benefit from this. (* This method will eventually be moved out of Vectorization.cpp, which isn't the right long-term home for it.)
* Restore the original behaviour in `vectorizeAsInsertSliceOp`, whereby the `in_bounds` attribute was used to identify potentially out-of-bounds accesses. Masks are only used when input vector sizes are specified. * Revert the changes in insert-slice-with-patterns.mlir and pad-with-patterns.mlir, i.e. the tests in which we don't specify vector sizes. * Other minor updates.
…code reuse * Restore the changes in transform-e2e.mlir + transform-vector.mlir * Updated in_bounds attribute calculation in `createWriteOrMaskedWrite` - otherwise transform-e2e.mlir goes into an infite loop. I will create a repro and open a GitHub issue before landing this. * The in_bounds attribute calculaiton is incorrect and I will create a GitHub ticket to fix it before merging this. See the comments in this patch.
42b1783
to
373036e
Compare
Update 30/5/25 Updated the summary and rebased. I’ve made some small tweaks so that I could revert the changes made to
As noted in the summary, I did identify some issues - these are tracked here: I’ll be away next week, so I plan to wait until I’m back before landing this. That also gives other potential reviewers some time to take a look :) If there are no new comments by then, I’ll go ahead and merge. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
This patch refactors two vectorization hooks in Vectorization.cpp:
createWriteOrMaskedWrite
gains a new parameter for write indices,aligning it with its counterpart
createReadOrMaskedRead
.vectorizeAsInsertSliceOp
is updated to reuse both of the abovehooks, rather than re-implementing similar logic.
CONTEXT
This is effectively a refactoring of the logic for vectorizing
tensor.insert_slice
. Recent updates added masking support:tensor.insert_slice
(1/N) #122927tensor.insert_slice
(2/N) #123031At the time, reuse of the shared
create*
hooks wasn't feasible due tomissing parameters and overly rigid assumptions. This patch resolves
that and moves us closer to a more maintainable structure.
CHANGES IN
createWriteOrMaskedWrite
vector to store, via named variables like
destType
/vecToStoreType
,destShape
/vecToStoreShape
, etc.in_bounds
. For example, the size of thein_bounds
attr now matchesthe source vector rank, not the tensor rank.
vecToStoreRank == destRank
- this doesn'thold in many real examples.
vecToStoreShape
(vector) instead ofdestShape
(tensor). (Eventually we should not requireinputVecSizesForLeadingDims
at all - mask shape should be inferred.)NEW HELPER:
isMaskTriviallyFoldable
Adds a utility to detect when masking is unnecessary. This avoids
inserting redundant masks and reduces the burden on canonicalization to
clean them up later.
Example where masking is provably unnecessary:
Also, without this hook, tests are more complicated and require more
matching.
VECTORIZATION BEHAVIOUR
This patch preserves the current behaviour around masking and the use
of
in_bounds
attribute. Specifically:useInBoundsInsteadOfMasking
is set when no input vector sizes areavailable.
Note: the computation of the
in_bounds
attribute is not always correct. Thatissue is tracked here:
This will be addressed separately.
TEST CHANGES
Only affects vectorization of:
tensor.insert_slice
(now refactored to use shared hooks)Test diffs involve additional
arith.constant
Ops due to increased reuse ofshared helpers (which generate their own constants). This will be cleaned up
via constant caching (see #138265).
NOTE FOR REVIEWERS
This is a fairly substantial rewrite. You may find it easier to review
createWriteOrMaskedWrite
as a new method rather than diffingline-by-line.
TODOs (future PRs)
Further alignment of
createWriteOrMaskedWrite
andcreateReadOrMaskedRead
:createWriteOrMaskedWrite
next tocreateReadOrMaskedRead
(inVectorUtils.cpp)
createReadOrMaskedRead
leverageisMaskTriviallyFoldable
.isMaskTriviallyFoldable
with value-bounds-analysis. See theupdated test in transform-vector.mlir for an example that would
benefit from this.
(*) This method will eventually be moved out of Vectorization.cpp, which
isn't the right long-term home for it.