[Mlir-commits] [mlir] [mlir][vector][NFC] Drop 0-d guards in transfer permutation lowering (PR #200703)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Sun May 31 17:38:57 PDT 2026


https://github.com/SeongjaeP created https://github.com/llvm/llvm-project/pull/200703

Four patterns registered by
`populateVectorTransferPermutationMapLoweringPatterns` (`TransferReadPermutationLowering`, `TransferWritePermutationLowering`, `TransferWriteNonPermutationLowering`, `TransferOpReduceRank`) each short-circuit with an explicit `if (op.getTransferRank() == 0)` bail-out marked `// TODO: support 0-d corner case.`. The TODOs date back to 2021 and have not been touched since.

0-d transfers are already supported elsewhere in the lowering pipeline: `TransferReadToVectorLoadLowering` / `TransferWriteToVectorStoreLowering` explicitly pass them through ("We let the 0-d corner case pass-through as it is supported."), and `--convert-vector-to-llvm` lowers `vector.transfer_read ... : memref<...>, vector<f32>` to a `llvm.load ... -> vector<1xf32>`.

The four permutation patterns are mid-level rewrites that hoist permutations, transposes, and leading broadcasts out into separate ops. A rank-0 transfer has nothing to permute, transpose, or rank-reduce, so none of these patterns can perform a meaningful rewrite on it. The downstream checks already filter the 0-d case naturally:

- `TransferReadPermutationLowering` -> `map.getNumResults() == 0`.
- `TransferWritePermutationLowering` -> `map.isMinorIdentity()` is vacuously true on a rank-0 map.
- `TransferWriteNonPermutationLowering` -> `map.isPermutationOfMinorIdentityWithBroadcasting(...)` is vacuously true on a rank-0 map.
- `TransferOpReduceRank` -> the loop over `map.getResults()` is empty, so `numLeadingBroadcast == 0`.

Drop the explicit guards and let the existing checks filter 0-d, with a short comment at each site pointing at the path. The only observable difference is the failure diagnostic (e.g. "0 result permutation map" instead of "0-d corner case not supported"), which is more specific about the actual reason the pattern does not apply.

Add a LIT test in `vector-transfer-permutation-lowering.mlir` covering 0-d `transfer_read` and `transfer_write` to lock in that the patterns remain a no-op on rank-0 transfers.

No functional change.

>From f16dc56aec456e5ffdda35a92f1c403a3c3449f0 Mon Sep 17 00:00:00 2001
From: "sjae.park" <dev at opt-ai.kr>
Date: Sun, 31 May 2026 23:45:24 +0000
Subject: [PATCH] [mlir][vector][NFC] Drop 0-d guards in transfer permutation
 lowering

Four patterns registered by
`populateVectorTransferPermutationMapLoweringPatterns`
(`TransferReadPermutationLowering`, `TransferWritePermutationLowering`,
`TransferWriteNonPermutationLowering`, `TransferOpReduceRank`) each
short-circuit with an explicit `if (op.getTransferRank() == 0)`
bail-out marked `// TODO: support 0-d corner case.`. The TODOs date
back to 2021 and have not been touched since.

0-d transfers are already supported elsewhere in the lowering pipeline:
`TransferReadToVectorLoadLowering` / `TransferWriteToVectorStoreLowering`
explicitly pass them through ("We let the 0-d corner case pass-through
as it is supported."), and `--convert-vector-to-llvm` lowers
`vector.transfer_read ... : memref<...>, vector<f32>` to a
`llvm.load ... -> vector<1xf32>`.

The four permutation patterns are mid-level rewrites that hoist
permutations, transposes, and leading broadcasts out into separate ops.
A rank-0 transfer has nothing to permute, transpose, or rank-reduce,
so none of these patterns can perform a meaningful rewrite on it. The
downstream checks already filter the 0-d case naturally:

- `TransferReadPermutationLowering` -> `map.getNumResults() == 0`.
- `TransferWritePermutationLowering` -> `map.isMinorIdentity()` is
  vacuously true on a rank-0 map.
- `TransferWriteNonPermutationLowering` ->
  `map.isPermutationOfMinorIdentityWithBroadcasting(...)` is vacuously
  true on a rank-0 map.
- `TransferOpReduceRank` -> the loop over `map.getResults()` is empty,
  so `numLeadingBroadcast == 0`.

Drop the explicit guards and let the existing checks filter 0-d, with a
short comment at each site pointing at the path. The only observable
difference is the failure diagnostic (e.g. "0 result permutation map"
instead of "0-d corner case not supported"), which is more specific
about the actual reason the pattern does not apply.

Add a LIT test in `vector-transfer-permutation-lowering.mlir` covering
0-d `transfer_read` and `transfer_write` to lock in that the patterns
remain a no-op on rank-0 transfers.

No functional change.
---
 .../Vector/Transforms/LowerVectorTransfer.cpp | 18 ++++--------
 .../vector-transfer-permutation-lowering.mlir | 28 +++++++++++++++++++
 2 files changed, 34 insertions(+), 12 deletions(-)

diff --git a/mlir/lib/Dialect/Vector/Transforms/LowerVectorTransfer.cpp b/mlir/lib/Dialect/Vector/Transforms/LowerVectorTransfer.cpp
index 2cf8f0beaa4de..7ba9e7c2ade5c 100644
--- a/mlir/lib/Dialect/Vector/Transforms/LowerVectorTransfer.cpp
+++ b/mlir/lib/Dialect/Vector/Transforms/LowerVectorTransfer.cpp
@@ -94,15 +94,13 @@ struct TransferReadPermutationLowering
   matchAndRewriteMaskableOp(vector::TransferReadOp op,
                             MaskingOpInterface maskOp,
                             PatternRewriter &rewriter) const override {
-    // TODO: support 0-d corner case.
-    if (op.getTransferRank() == 0)
-      return rewriter.notifyMatchFailure(op, "0-d corner case not supported");
     // TODO: Support transfer_read inside MaskOp case.
     if (maskOp)
       return rewriter.notifyMatchFailure(op, "Masked case not supported");
 
     SmallVector<unsigned> permutation;
     AffineMap map = op.getPermutationMap();
+    // 0-d transfers have a 0-result permutation map and are filtered here.
     if (map.getNumResults() == 0)
       return rewriter.notifyMatchFailure(op, "0 result permutation map");
     if (!map.isPermutationOfMinorIdentityWithBroadcasting(permutation)) {
@@ -172,15 +170,14 @@ struct TransferWritePermutationLowering
   matchAndRewriteMaskableOp(vector::TransferWriteOp op,
                             MaskingOpInterface maskOp,
                             PatternRewriter &rewriter) const override {
-    // TODO: support 0-d corner case.
-    if (op.getTransferRank() == 0)
-      return rewriter.notifyMatchFailure(op, "0-d corner case not supported");
     // TODO: Support transfer_write inside MaskOp case.
     if (maskOp)
       return rewriter.notifyMatchFailure(op, "Masked case not supported");
 
     SmallVector<unsigned> permutation;
     AffineMap map = op.getPermutationMap();
+    // 0-d transfers have a vacuously minor-identity permutation map and are
+    // filtered here.
     if (map.isMinorIdentity())
       return rewriter.notifyMatchFailure(op, "map is already minor identity");
 
@@ -244,15 +241,14 @@ struct TransferWriteNonPermutationLowering
   matchAndRewriteMaskableOp(vector::TransferWriteOp op,
                             MaskingOpInterface maskOp,
                             PatternRewriter &rewriter) const override {
-    // TODO: support 0-d corner case.
-    if (op.getTransferRank() == 0)
-      return rewriter.notifyMatchFailure(op, "0-d corner case not supported");
     // TODO: Support transfer_write inside MaskOp case.
     if (maskOp)
       return rewriter.notifyMatchFailure(op, "Masked case not supported");
 
     SmallVector<unsigned> permutation;
     AffineMap map = op.getPermutationMap();
+    // 0-d transfers vacuously satisfy the minor-identity-with-broadcasting
+    // check and are filtered here.
     if (map.isPermutationOfMinorIdentityWithBroadcasting(permutation)) {
       return rewriter.notifyMatchFailure(
           op,
@@ -323,9 +319,6 @@ struct TransferOpReduceRank
   matchAndRewriteMaskableOp(vector::TransferReadOp op,
                             MaskingOpInterface maskOp,
                             PatternRewriter &rewriter) const override {
-    // TODO: support 0-d corner case.
-    if (op.getTransferRank() == 0)
-      return rewriter.notifyMatchFailure(op, "0-d corner case not supported");
     // TODO: support masked case.
     if (maskOp)
       return rewriter.notifyMatchFailure(op, "Masked case not supported");
@@ -338,6 +331,7 @@ struct TransferOpReduceRank
         break;
       numLeadingBroadcast++;
     }
+    // 0-d transfers iterate the empty result list and hit this `0` check.
     // If there are no leading zeros in the map there is nothing to do.
     if (numLeadingBroadcast == 0)
       return rewriter.notifyMatchFailure(op, "no leading broadcasts in map");
diff --git a/mlir/test/Dialect/Vector/vector-transfer-permutation-lowering.mlir b/mlir/test/Dialect/Vector/vector-transfer-permutation-lowering.mlir
index 3ae18835c8367..91a5762529a50 100644
--- a/mlir/test/Dialect/Vector/vector-transfer-permutation-lowering.mlir
+++ b/mlir/test/Dialect/Vector/vector-transfer-permutation-lowering.mlir
@@ -478,6 +478,34 @@ func.func @xfer_read_minor_identitiy_bcast_dims_masked(
   return %res : vector<8x4x2x3xf32>
 }
 
+///----------------------------------------------------------------------------------------
+/// [0-d corner case: no pattern applies]
+///
+/// 0-d transfers have a rank-0 result vector. There is no permutation,
+/// transpose, or leading broadcast to lower, so none of the patterns above
+/// should match. The downstream checks in each pattern filter these out:
+///   - TransferReadPermutationLowering: 0-result permutation map.
+///   - TransferWritePermutationLowering: vacuously minor identity.
+///   - TransferWriteNonPermutationLowering: vacuously projected permutation.
+///   - TransferOpReduceRank: no leading broadcast in an empty result map.
+///----------------------------------------------------------------------------------------
+
+// CHECK-LABEL: func @xfer_read_0d_unchanged
+//       CHECK:   %[[V:.+]] = vector.transfer_read %{{.*}}, %{{.*}} : memref<?x?xf32>, vector<f32>
+//       CHECK:   return %[[V]] : vector<f32>
+func.func @xfer_read_0d_unchanged(%mem: memref<?x?xf32>, %idx: index) -> vector<f32> {
+  %pad = arith.constant 0.0 : f32
+  %v = vector.transfer_read %mem[%idx, %idx], %pad : memref<?x?xf32>, vector<f32>
+  return %v : vector<f32>
+}
+
+// CHECK-LABEL: func @xfer_write_0d_unchanged
+//       CHECK:   vector.transfer_write %{{.*}}, %{{.*}}[%{{.*}}, %{{.*}}] : vector<f32>, memref<?x?xf32>
+func.func @xfer_write_0d_unchanged(%val: vector<f32>, %mem: memref<?x?xf32>, %idx: index) {
+  vector.transfer_write %val, %mem[%idx, %idx] : vector<f32>, memref<?x?xf32>
+  return
+}
+
 ///----------------------------------------------------------------------------------------
 //  TD sequence
 ///----------------------------------------------------------------------------------------



More information about the Mlir-commits mailing list