[Mlir-commits] [mlir] fc81a66 - [NFC][Linalg] Add `matchConvolutionOpOfType` API and make `isaConvolutionOpOfType` API a wrapper (#174722)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Tue Jan 13 22:20:38 PST 2026


Author: Abhishek Varma
Date: 2026-01-14T06:20:33Z
New Revision: fc81a665133fe757741d68f07245d5f696a65346

URL: https://github.com/llvm/llvm-project/commit/fc81a665133fe757741d68f07245d5f696a65346
DIFF: https://github.com/llvm/llvm-project/commit/fc81a665133fe757741d68f07245d5f696a65346.diff

LOG: [NFC][Linalg] Add `matchConvolutionOpOfType` API and make `isaConvolutionOpOfType` API a wrapper (#174722)

-- This commit involves the following updates pertaining to
`isaConvolutionOpOfType` API :-
1. We don't want dilations/strides of convolution op to be returned as
pointer arguments to the API function - to tackle this we create a new
API `matchConvolutionOpOfType` which would return an optional struct of
dilations/stride.
2. To not break the original API's use case as a simple querying
functionality with true/false return - we keep `isaConvolutionOpOfType`
as a wrapper API which will invoke `matchConvolutionOpOfType` API and
return true/false depending on whether `matchConvolutionOpOfType` API
returned any value or not.
3. Dilations/strides of named convolution op are also populated now (it
was missed in the previous PRs while creating `isaConvolutionOpOfType`).
4. [Max/Min]UnsignedPool ops' body matcher now only matches unsigned int
ops (refer: https://github.com/llvm/llvm-project/pull/166070)

-- No tests are being added as all the above are NFC changes around the
API which already is being tested via Specialize.cpp.

Signed-off-by: Abhishek Varma <abhvarma at amd.com>

Added: 
    

Modified: 
    mlir/include/mlir/Dialect/Linalg/Utils/Utils.h
    mlir/lib/Dialect/Linalg/Transforms/Specialize.cpp
    mlir/lib/Dialect/Linalg/Utils/Utils.cpp

Removed: 
    


################################################################################
diff  --git a/mlir/include/mlir/Dialect/Linalg/Utils/Utils.h b/mlir/include/mlir/Dialect/Linalg/Utils/Utils.h
index 9da01f30b52d2..06e7a472a8182 100644
--- a/mlir/include/mlir/Dialect/Linalg/Utils/Utils.h
+++ b/mlir/include/mlir/Dialect/Linalg/Utils/Utils.h
@@ -106,12 +106,26 @@ getReassociationMapForFoldingUnitDims(ArrayRef<OpFoldResult> mixedSizes);
 // Convolution matcher utility
 //===----------------------------------------------------------------------===//
 
-/// Given a linalg `op` this function returns true if it is a convolution op of
-/// type `ConvOpTy` and populates `dilations` and `strides` with values inferred
-/// from the indexing maps.
+/// A struct containing dilations and strides inferred from convolution ops.
+struct DilationsAndStrides {
+  SmallVector<int64_t> dilations;
+  SmallVector<int64_t> strides;
+};
+
+/// Given a linalg `op` this function returns DilationsAndStrides if it is a
+/// convolution op of type `ConvOpTy`, otherwise returns std::nullopt. The
+/// dilations and strides are inferred from the indexing maps. For ops like
+/// Conv1DOp, Conv2DOp and Conv3DOp that have no strides/dilations attributes,
+/// defaults of [1, ...] are returned for both.
 template <typename ConvOpTy>
-bool isaConvolutionOpOfType(LinalgOp op, SmallVector<int64_t> *dilations,
-                            SmallVector<int64_t> *strides);
+std::optional<DilationsAndStrides> matchConvolutionOpOfType(LinalgOp op);
+
+/// Returns true if the linalg `op` is a convolution op of type `ConvOpTy`.
+/// This is a convenience wrapper around matchConvolutionOpOfType.
+template <typename ConvOpTy>
+bool isaConvolutionOpOfType(LinalgOp op) {
+  return matchConvolutionOpOfType<ConvOpTy>(op).has_value();
+}
 
 //===----------------------------------------------------------------------===//
 // Fusion / Tiling utilities

diff  --git a/mlir/lib/Dialect/Linalg/Transforms/Specialize.cpp b/mlir/lib/Dialect/Linalg/Transforms/Specialize.cpp
index 0c7b998ffcab9..da062152f61ad 100644
--- a/mlir/lib/Dialect/Linalg/Transforms/Specialize.cpp
+++ b/mlir/lib/Dialect/Linalg/Transforms/Specialize.cpp
@@ -267,11 +267,11 @@ specializeToConvOp(RewriterBase &rewriter, GenericOp genericOp,
 /// Converts linalg.generic to named linalg.*conv/pooling* where possible.
 static FailureOr<LinalgOp> specializeLinalgConvolutions(RewriterBase &rewriter,
                                                         GenericOp genericOp) {
-  SmallVector<int64_t> dilations, strides;
 #define CONV_OP_SPECIALIZER(ConvOpTy)                                          \
-  if (isaConvolutionOpOfType<ConvOpTy>(genericOp, &dilations, &strides))       \
-    return specializeToConvOp<ConvOpTy>(rewriter, genericOp, dilations,        \
-                                        strides);                              \
+  if (std::optional<DilationsAndStrides> convParams =                          \
+          matchConvolutionOpOfType<ConvOpTy>(genericOp))                       \
+    return specializeToConvOp<ConvOpTy>(                                       \
+        rewriter, genericOp, convParams->dilations, convParams->strides);      \
   // -----------------------------
   // Convolution ops.
   // -----------------------------

diff  --git a/mlir/lib/Dialect/Linalg/Utils/Utils.cpp b/mlir/lib/Dialect/Linalg/Utils/Utils.cpp
index 2718124251c18..4e1731ddcfd45 100644
--- a/mlir/lib/Dialect/Linalg/Utils/Utils.cpp
+++ b/mlir/lib/Dialect/Linalg/Utils/Utils.cpp
@@ -373,11 +373,8 @@ static bool bodyMatcherForMaxSignedPoolOps(Value yieldVal, Block *body) {
                                                                   body);
 }
 
-// max_unsigned ops should not allow float data type.
-// TODO(#164800): Retire OPDSL logic.
 static bool bodyMatcherForMaxUnsignedPoolOps(Value yieldVal, Block *body) {
-  return bodyMatcherForPoolOps<arith::MaximumFOp, arith::MaxUIOp>(yieldVal,
-                                                                  body);
+  return bodyMatcherForPoolOps<arith::MaxUIOp>(yieldVal, body);
 }
 
 static bool bodyMatcherForMinSignedPoolOps(Value yieldVal, Block *body) {
@@ -385,11 +382,8 @@ static bool bodyMatcherForMinSignedPoolOps(Value yieldVal, Block *body) {
                                                                   body);
 }
 
-// min_unsigned ops should not allow float data type.
-// TODO(#164800): Retire OPDSL logic.
 static bool bodyMatcherForMinUnsignedPoolOps(Value yieldVal, Block *body) {
-  return bodyMatcherForPoolOps<arith::MinimumFOp, arith::MinUIOp>(yieldVal,
-                                                                  body);
+  return bodyMatcherForPoolOps<arith::MinUIOp>(yieldVal, body);
 }
 
 static bool bodyMatcherForSumPoolOps(Value yieldVal, Block *body) {
@@ -598,109 +592,142 @@ class ConvMatcherBuilder {
 //===----------------------------------------------------------------------===//
 
 template <>
-bool isaConvolutionOpOfType<linalg::Conv1DOp>(LinalgOp op,
-                                              SmallVector<int64_t> *dilations,
-                                              SmallVector<int64_t> *strides) {
-  if (isa<linalg::Conv1DOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::Conv1DOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (isa<linalg::Conv1DOp>(op)) {
+    // Conv1DOp has no strides/dilations attributes, default to 1.
+    result.dilations = SmallVector<int64_t>(1, 1);
+    result.strides = SmallVector<int64_t>(1, 1);
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/1, dilations, strides);
+  ConvMatcherBuilder m(op, /*spatialRank=*/1, &result.dilations,
+                       &result.strides);
   AffineExpr W = m.dim(0);
   AffineExpr w = m.dim(1);
 
-  return m.matchStride(/*iDim=*/0, /*fDim=*/0, /*oDim=*/0, /*idx=*/0)
-      .matchMaps({/*inputMap=*/{m.strided(W, w, 0)},
-                  /*filterMap=*/{w},
-                  /*outputMap=*/{W}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/0, /*fDim=*/0, /*oDim=*/0, /*idx=*/0)
+          .matchMaps({/*inputMap=*/{m.strided(W, w, 0)},
+                      /*filterMap=*/{w},
+                      /*outputMap=*/{W}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::Conv1DNwcWcfOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::Conv1DNwcWcfOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::Conv1DNwcWcfOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto convOp = dyn_cast<linalg::Conv1DNwcWcfOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(convOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(convOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/1, dilations, strides);
+  ConvMatcherBuilder m(op, /*spatialRank=*/1, &result.dilations,
+                       &result.strides);
   AffineExpr N = m.dim(0);
   AffineExpr W = m.dim(1);
   AffineExpr F = m.dim(2);
   AffineExpr w = m.dim(3);
   AffineExpr c = m.dim(4);
 
-  return m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
-      .matchMaps({/*inputMap=*/{N, m.strided(W, w, 0), c},
-                  /*filterMap=*/{w, c, F},
-                  /*outputMap=*/{N, W, F}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
+          .matchMaps({/*inputMap=*/{N, m.strided(W, w, 0), c},
+                      /*filterMap=*/{w, c, F},
+                      /*outputMap=*/{N, W, F}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::Conv1DNcwFcwOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::Conv1DNcwFcwOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::Conv1DNcwFcwOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto convOp = dyn_cast<linalg::Conv1DNcwFcwOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(convOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(convOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/1, dilations, strides);
+  ConvMatcherBuilder m(op, /*spatialRank=*/1, &result.dilations,
+                       &result.strides);
   AffineExpr N = m.dim(0);
   AffineExpr F = m.dim(1);
   AffineExpr W = m.dim(2);
   AffineExpr c = m.dim(3);
   AffineExpr w = m.dim(4);
 
-  return m.matchStride(/*iDim=*/2, /*fDim=*/2, /*oDim=*/2, /*idx=*/0)
-      .matchMaps({/*inputMap=*/{N, c, m.strided(W, w, 0)},
-                  /*filterMap=*/{F, c, w},
-                  /*outputMap=*/{N, F, W}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/2, /*fDim=*/2, /*oDim=*/2, /*idx=*/0)
+          .matchMaps({/*inputMap=*/{N, c, m.strided(W, w, 0)},
+                      /*filterMap=*/{F, c, w},
+                      /*outputMap=*/{N, F, W}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::Conv2DOp>(LinalgOp op,
-                                              SmallVector<int64_t> *dilations,
-                                              SmallVector<int64_t> *strides) {
-  if (isa<linalg::Conv2DOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::Conv2DOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (isa<linalg::Conv2DOp>(op)) {
+    // Conv2DOp has no strides/dilations attributes, default to 1.
+    result.dilations = SmallVector<int64_t>(2, 1);
+    result.strides = SmallVector<int64_t>(2, 1);
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/2, dilations, strides);
+  ConvMatcherBuilder m(op, /*spatialRank=*/2, &result.dilations,
+                       &result.strides);
   AffineExpr H = m.dim(0);
   AffineExpr W = m.dim(1);
   AffineExpr h = m.dim(2);
   AffineExpr w = m.dim(3);
 
-  return m.matchStride(/*iDim=*/0, /*fDim=*/0, /*oDim=*/0, /*idx=*/0)
-      .matchStride(/*iDim=*/1, /*fDim=*/1, /*oDim=*/1, /*idx=*/1)
-      .matchMaps({/*inputMap=*/{m.strided(H, h, 0), m.strided(W, w, 1)},
-                  /*filterMap=*/{h, w},
-                  /*outputMap=*/{H, W}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/0, /*fDim=*/0, /*oDim=*/0, /*idx=*/0)
+          .matchStride(/*iDim=*/1, /*fDim=*/1, /*oDim=*/1, /*idx=*/1)
+          .matchMaps({/*inputMap=*/{m.strided(H, h, 0), m.strided(W, w, 1)},
+                      /*filterMap=*/{h, w},
+                      /*outputMap=*/{H, W}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::Conv2DNhwcHwcfOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::Conv2DNhwcHwcfOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::Conv2DNhwcHwcfOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto convOp = dyn_cast<linalg::Conv2DNhwcHwcfOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(convOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(convOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/2, dilations, strides);
+  ConvMatcherBuilder m(op, /*spatialRank=*/2, &result.dilations,
+                       &result.strides);
   AffineExpr N = m.dim(0);
   AffineExpr H = m.dim(1);
   AffineExpr W = m.dim(2);
@@ -709,25 +736,33 @@ bool isaConvolutionOpOfType<linalg::Conv2DNhwcHwcfOp>(
   AffineExpr w = m.dim(5);
   AffineExpr c = m.dim(6);
 
-  return m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
-      .matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/1)
-      .matchMaps({/*inputMap=*/{N, m.strided(H, h, 0), m.strided(W, w, 1), c},
-                  /*filterMap=*/{h, w, c, F},
-                  /*outputMap=*/{N, H, W, F}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
+          .matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/1)
+          .matchMaps(
+              {/*inputMap=*/{N, m.strided(H, h, 0), m.strided(W, w, 1), c},
+               /*filterMap=*/{h, w, c, F},
+               /*outputMap=*/{N, H, W, F}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::Conv2DNhwcHwcfQOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::Conv2DNhwcHwcfQOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::Conv2DNhwcHwcfQOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto convOp = dyn_cast<linalg::Conv2DNhwcHwcfQOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(convOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(convOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/2, dilations, strides);
+  ConvMatcherBuilder m(op, /*spatialRank=*/2, &result.dilations,
+                       &result.strides);
   AffineExpr N = m.dim(0);
   AffineExpr H = m.dim(1);
   AffineExpr W = m.dim(2);
@@ -736,27 +771,35 @@ bool isaConvolutionOpOfType<linalg::Conv2DNhwcHwcfQOp>(
   AffineExpr w = m.dim(5);
   AffineExpr c = m.dim(6);
 
-  return m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
-      .matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/1)
-      .matchMaps({/*inputMap=*/{N, m.strided(H, h, 0), m.strided(W, w, 1), c},
-                  /*filterMap=*/{h, w, c, F},
-                  /*scalarMap=*/{},
-                  /*scalarMap=*/{},
-                  /*outputMap=*/{N, H, W, F}})
-      .matchBody(/*containsZeroPointOffset=*/true);
+  if (m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
+          .matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/1)
+          .matchMaps(
+              {/*inputMap=*/{N, m.strided(H, h, 0), m.strided(W, w, 1), c},
+               /*filterMap=*/{h, w, c, F},
+               /*scalarMap=*/{},
+               /*scalarMap=*/{},
+               /*outputMap=*/{N, H, W, F}})
+          .matchBody(/*containsZeroPointOffset=*/true))
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::Conv2DNhwcFhwcOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::Conv2DNhwcFhwcOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::Conv2DNhwcFhwcOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto convOp = dyn_cast<linalg::Conv2DNhwcFhwcOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(convOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(convOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/2, dilations, strides);
+  ConvMatcherBuilder m(op, /*spatialRank=*/2, &result.dilations,
+                       &result.strides);
   AffineExpr N = m.dim(0);
   AffineExpr H = m.dim(1);
   AffineExpr W = m.dim(2);
@@ -765,25 +808,33 @@ bool isaConvolutionOpOfType<linalg::Conv2DNhwcFhwcOp>(
   AffineExpr w = m.dim(5);
   AffineExpr c = m.dim(6);
 
-  return m.matchStride(/*iDim=*/1, /*fDim=*/1, /*oDim=*/1, /*idx=*/0)
-      .matchStride(/*iDim=*/2, /*fDim=*/2, /*oDim=*/2, /*idx=*/1)
-      .matchMaps({/*inputMap=*/{N, m.strided(H, h, 0), m.strided(W, w, 1), c},
-                  /*filterMap=*/{F, h, w, c},
-                  /*outputMap=*/{N, H, W, F}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/1, /*fDim=*/1, /*oDim=*/1, /*idx=*/0)
+          .matchStride(/*iDim=*/2, /*fDim=*/2, /*oDim=*/2, /*idx=*/1)
+          .matchMaps(
+              {/*inputMap=*/{N, m.strided(H, h, 0), m.strided(W, w, 1), c},
+               /*filterMap=*/{F, h, w, c},
+               /*outputMap=*/{N, H, W, F}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::Conv2DNhwcFhwcQOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::Conv2DNhwcFhwcQOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::Conv2DNhwcFhwcQOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto convOp = dyn_cast<linalg::Conv2DNhwcFhwcQOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(convOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(convOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/2, dilations, strides);
+  ConvMatcherBuilder m(op, /*spatialRank=*/2, &result.dilations,
+                       &result.strides);
   AffineExpr N = m.dim(0);
   AffineExpr H = m.dim(1);
   AffineExpr W = m.dim(2);
@@ -792,27 +843,35 @@ bool isaConvolutionOpOfType<linalg::Conv2DNhwcFhwcQOp>(
   AffineExpr w = m.dim(5);
   AffineExpr c = m.dim(6);
 
-  return m.matchStride(/*iDim=*/1, /*fDim=*/1, /*oDim=*/1, /*idx=*/0)
-      .matchStride(/*iDim=*/2, /*fDim=*/2, /*oDim=*/2, /*idx=*/1)
-      .matchMaps({/*inputMap=*/{N, m.strided(H, h, 0), m.strided(W, w, 1), c},
-                  /*filterMap=*/{F, h, w, c},
-                  /*scalarMap=*/{},
-                  /*scalarMap=*/{},
-                  /*outputMap=*/{N, H, W, F}})
-      .matchBody(/*containsZeroPointOffset=*/true);
+  if (m.matchStride(/*iDim=*/1, /*fDim=*/1, /*oDim=*/1, /*idx=*/0)
+          .matchStride(/*iDim=*/2, /*fDim=*/2, /*oDim=*/2, /*idx=*/1)
+          .matchMaps(
+              {/*inputMap=*/{N, m.strided(H, h, 0), m.strided(W, w, 1), c},
+               /*filterMap=*/{F, h, w, c},
+               /*scalarMap=*/{},
+               /*scalarMap=*/{},
+               /*outputMap=*/{N, H, W, F}})
+          .matchBody(/*containsZeroPointOffset=*/true))
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::Conv2DNchwFchwOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::Conv2DNchwFchwOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::Conv2DNchwFchwOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto convOp = dyn_cast<linalg::Conv2DNchwFchwOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(convOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(convOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/2, dilations, strides);
+  ConvMatcherBuilder m(op, /*spatialRank=*/2, &result.dilations,
+                       &result.strides);
   AffineExpr N = m.dim(0);
   AffineExpr F = m.dim(1);
   AffineExpr H = m.dim(2);
@@ -821,25 +880,33 @@ bool isaConvolutionOpOfType<linalg::Conv2DNchwFchwOp>(
   AffineExpr h = m.dim(5);
   AffineExpr w = m.dim(6);
 
-  return m.matchStride(/*iDim=*/2, /*fDim=*/2, /*oDim=*/2, /*idx=*/0)
-      .matchStride(/*iDim=*/3, /*fDim=*/3, /*oDim=*/3, /*idx=*/1)
-      .matchMaps({/*inputMap=*/{N, c, m.strided(H, h, 0), m.strided(W, w, 1)},
-                  /*filterMap=*/{F, c, h, w},
-                  /*outputMap=*/{N, F, H, W}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/2, /*fDim=*/2, /*oDim=*/2, /*idx=*/0)
+          .matchStride(/*iDim=*/3, /*fDim=*/3, /*oDim=*/3, /*idx=*/1)
+          .matchMaps(
+              {/*inputMap=*/{N, c, m.strided(H, h, 0), m.strided(W, w, 1)},
+               /*filterMap=*/{F, c, h, w},
+               /*outputMap=*/{N, F, H, W}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::Conv2DNchwFchwQOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::Conv2DNchwFchwQOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::Conv2DNchwFchwQOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto convOp = dyn_cast<linalg::Conv2DNchwFchwQOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(convOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(convOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/2, dilations, strides);
+  ConvMatcherBuilder m(op, /*spatialRank=*/2, &result.dilations,
+                       &result.strides);
   AffineExpr N = m.dim(0);
   AffineExpr F = m.dim(1);
   AffineExpr H = m.dim(2);
@@ -848,27 +915,35 @@ bool isaConvolutionOpOfType<linalg::Conv2DNchwFchwQOp>(
   AffineExpr h = m.dim(5);
   AffineExpr w = m.dim(6);
 
-  return m.matchStride(/*iDim=*/2, /*fDim=*/2, /*oDim=*/2, /*idx=*/0)
-      .matchStride(/*iDim=*/3, /*fDim=*/3, /*oDim=*/3, /*idx=*/1)
-      .matchMaps({/*inputMap=*/{N, c, m.strided(H, h, 0), m.strided(W, w, 1)},
-                  /*filterMap=*/{F, c, h, w},
-                  /*scalarMap=*/{},
-                  /*scalarMap=*/{},
-                  /*outputMap=*/{N, F, H, W}})
-      .matchBody(/*containsZeroPointOffset=*/true);
+  if (m.matchStride(/*iDim=*/2, /*fDim=*/2, /*oDim=*/2, /*idx=*/0)
+          .matchStride(/*iDim=*/3, /*fDim=*/3, /*oDim=*/3, /*idx=*/1)
+          .matchMaps(
+              {/*inputMap=*/{N, c, m.strided(H, h, 0), m.strided(W, w, 1)},
+               /*filterMap=*/{F, c, h, w},
+               /*scalarMap=*/{},
+               /*scalarMap=*/{},
+               /*outputMap=*/{N, F, H, W}})
+          .matchBody(/*containsZeroPointOffset=*/true))
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::Conv2DNgchwFgchwOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::Conv2DNgchwFgchwOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::Conv2DNgchwFgchwOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto convOp = dyn_cast<linalg::Conv2DNgchwFgchwOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(convOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(convOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/2, dilations, strides);
+  ConvMatcherBuilder m(op, /*spatialRank=*/2, &result.dilations,
+                       &result.strides);
   AffineExpr N = m.dim(0);
   AffineExpr G = m.dim(1);
   AffineExpr F = m.dim(2);
@@ -878,26 +953,33 @@ bool isaConvolutionOpOfType<linalg::Conv2DNgchwFgchwOp>(
   AffineExpr h = m.dim(6);
   AffineExpr w = m.dim(7);
 
-  return m.matchStride(/*iDim=*/3, /*fDim=*/3, /*oDim=*/3, /*idx=*/0)
-      .matchStride(/*iDim=*/4, /*fDim=*/4, /*oDim=*/4, /*idx=*/1)
-      .matchMaps(
-          {/*inputMap=*/{N, G, c, m.strided(H, h, 0), m.strided(W, w, 1)},
-           /*filterMap=*/{F, G, c, h, w},
-           /*outputMap=*/{N, G, F, H, W}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/3, /*fDim=*/3, /*oDim=*/3, /*idx=*/0)
+          .matchStride(/*iDim=*/4, /*fDim=*/4, /*oDim=*/4, /*idx=*/1)
+          .matchMaps(
+              {/*inputMap=*/{N, G, c, m.strided(H, h, 0), m.strided(W, w, 1)},
+               /*filterMap=*/{F, G, c, h, w},
+               /*outputMap=*/{N, G, F, H, W}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::Conv2DNgchwGfchwOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::Conv2DNgchwGfchwOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::Conv2DNgchwGfchwOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto convOp = dyn_cast<linalg::Conv2DNgchwGfchwOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(convOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(convOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/2, dilations, strides);
+  ConvMatcherBuilder m(op, /*spatialRank=*/2, &result.dilations,
+                       &result.strides);
   AffineExpr N = m.dim(0);
   AffineExpr G = m.dim(1);
   AffineExpr F = m.dim(2);
@@ -907,26 +989,33 @@ bool isaConvolutionOpOfType<linalg::Conv2DNgchwGfchwOp>(
   AffineExpr h = m.dim(6);
   AffineExpr w = m.dim(7);
 
-  return m.matchStride(/*iDim=*/3, /*fDim=*/3, /*oDim=*/3, /*idx=*/0)
-      .matchStride(/*iDim=*/4, /*fDim=*/4, /*oDim=*/4, /*idx=*/1)
-      .matchMaps(
-          {/*inputMap=*/{N, G, c, m.strided(H, h, 0), m.strided(W, w, 1)},
-           /*filterMap=*/{G, F, c, h, w},
-           /*outputMap=*/{N, G, F, H, W}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/3, /*fDim=*/3, /*oDim=*/3, /*idx=*/0)
+          .matchStride(/*iDim=*/4, /*fDim=*/4, /*oDim=*/4, /*idx=*/1)
+          .matchMaps(
+              {/*inputMap=*/{N, G, c, m.strided(H, h, 0), m.strided(W, w, 1)},
+               /*filterMap=*/{G, F, c, h, w},
+               /*outputMap=*/{N, G, F, H, W}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::Conv2DNgchwGfchwQOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::Conv2DNgchwGfchwQOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::Conv2DNgchwGfchwQOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto convOp = dyn_cast<linalg::Conv2DNgchwGfchwQOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(convOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(convOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/2, dilations, strides);
+  ConvMatcherBuilder m(op, /*spatialRank=*/2, &result.dilations,
+                       &result.strides);
   AffineExpr N = m.dim(0);
   AffineExpr G = m.dim(1);
   AffineExpr F = m.dim(2);
@@ -936,28 +1025,35 @@ bool isaConvolutionOpOfType<linalg::Conv2DNgchwGfchwQOp>(
   AffineExpr h = m.dim(6);
   AffineExpr w = m.dim(7);
 
-  return m.matchStride(/*iDim=*/3, /*fDim=*/3, /*oDim=*/3, /*idx=*/0)
-      .matchStride(/*iDim=*/4, /*fDim=*/4, /*oDim=*/4, /*idx=*/1)
-      .matchMaps(
-          {/*inputMap=*/{N, G, c, m.strided(H, h, 0), m.strided(W, w, 1)},
-           /*filterMap=*/{G, F, c, h, w},
-           /*scalarMap=*/{},
-           /*scalarMap=*/{},
-           /*outputMap=*/{N, G, F, H, W}})
-      .matchBody(/*containsZeroPointOffset=*/true);
+  if (m.matchStride(/*iDim=*/3, /*fDim=*/3, /*oDim=*/3, /*idx=*/0)
+          .matchStride(/*iDim=*/4, /*fDim=*/4, /*oDim=*/4, /*idx=*/1)
+          .matchMaps(
+              {/*inputMap=*/{N, G, c, m.strided(H, h, 0), m.strided(W, w, 1)},
+               /*filterMap=*/{G, F, c, h, w},
+               /*scalarMap=*/{},
+               /*scalarMap=*/{},
+               /*outputMap=*/{N, G, F, H, W}})
+          .matchBody(/*containsZeroPointOffset=*/true))
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::Conv2DNhwgcGfhwcOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::Conv2DNhwgcGfhwcOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::Conv2DNhwgcGfhwcOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto convOp = dyn_cast<linalg::Conv2DNhwgcGfhwcOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(convOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(convOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/2, dilations, strides);
+  ConvMatcherBuilder m(op, /*spatialRank=*/2, &result.dilations,
+                       &result.strides);
   AffineExpr N = m.dim(0);
   AffineExpr H = m.dim(1);
   AffineExpr W = m.dim(2);
@@ -967,26 +1063,33 @@ bool isaConvolutionOpOfType<linalg::Conv2DNhwgcGfhwcOp>(
   AffineExpr w = m.dim(6);
   AffineExpr c = m.dim(7);
 
-  return m.matchStride(/*iDim=*/1, /*fDim=*/2, /*oDim=*/1, /*idx=*/0)
-      .matchStride(/*iDim=*/2, /*fDim=*/3, /*oDim=*/2, /*idx=*/1)
-      .matchMaps(
-          {/*inputMap=*/{N, m.strided(H, h, 0), m.strided(W, w, 1), G, c},
-           /*filterMap=*/{G, F, h, w, c},
-           /*outputMap=*/{N, H, W, G, F}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/1, /*fDim=*/2, /*oDim=*/1, /*idx=*/0)
+          .matchStride(/*iDim=*/2, /*fDim=*/3, /*oDim=*/2, /*idx=*/1)
+          .matchMaps(
+              {/*inputMap=*/{N, m.strided(H, h, 0), m.strided(W, w, 1), G, c},
+               /*filterMap=*/{G, F, h, w, c},
+               /*outputMap=*/{N, H, W, G, F}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::Conv2DNhwgcGfhwcQOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::Conv2DNhwgcGfhwcQOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::Conv2DNhwgcGfhwcQOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto convOp = dyn_cast<linalg::Conv2DNhwgcGfhwcQOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(convOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(convOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/2, dilations, strides);
+  ConvMatcherBuilder m(op, /*spatialRank=*/2, &result.dilations,
+                       &result.strides);
   AffineExpr N = m.dim(0);
   AffineExpr H = m.dim(1);
   AffineExpr W = m.dim(2);
@@ -996,28 +1099,35 @@ bool isaConvolutionOpOfType<linalg::Conv2DNhwgcGfhwcQOp>(
   AffineExpr w = m.dim(6);
   AffineExpr c = m.dim(7);
 
-  return m.matchStride(/*iDim=*/1, /*fDim=*/2, /*oDim=*/1, /*idx=*/0)
-      .matchStride(/*iDim=*/2, /*fDim=*/3, /*oDim=*/2, /*idx=*/1)
-      .matchMaps(
-          {/*inputMap=*/{N, m.strided(H, h, 0), m.strided(W, w, 1), G, c},
-           /*filterMap=*/{G, F, h, w, c},
-           /*scalarMap=*/{},
-           /*scalarMap=*/{},
-           /*outputMap=*/{N, H, W, G, F}})
-      .matchBody(/*containsZeroPointOffset=*/true);
+  if (m.matchStride(/*iDim=*/1, /*fDim=*/2, /*oDim=*/1, /*idx=*/0)
+          .matchStride(/*iDim=*/2, /*fDim=*/3, /*oDim=*/2, /*idx=*/1)
+          .matchMaps(
+              {/*inputMap=*/{N, m.strided(H, h, 0), m.strided(W, w, 1), G, c},
+               /*filterMap=*/{G, F, h, w, c},
+               /*scalarMap=*/{},
+               /*scalarMap=*/{},
+               /*outputMap=*/{N, H, W, G, F}})
+          .matchBody(/*containsZeroPointOffset=*/true))
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::Conv3DOp>(LinalgOp op,
-                                              SmallVector<int64_t> *dilations,
-                                              SmallVector<int64_t> *strides) {
-  if (isa<linalg::Conv3DOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::Conv3DOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (isa<linalg::Conv3DOp>(op)) {
+    // Conv3DOp has no strides/dilations attributes, default to 1.
+    result.dilations = SmallVector<int64_t>(3, 1);
+    result.strides = SmallVector<int64_t>(3, 1);
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/3, dilations, strides);
+  ConvMatcherBuilder m(op, /*spatialRank=*/3, &result.dilations,
+                       &result.strides);
   AffineExpr D = m.dim(0);
   AffineExpr H = m.dim(1);
   AffineExpr W = m.dim(2);
@@ -1025,27 +1135,34 @@ bool isaConvolutionOpOfType<linalg::Conv3DOp>(LinalgOp op,
   AffineExpr h = m.dim(4);
   AffineExpr w = m.dim(5);
 
-  return m.matchStride(/*iDim=*/0, /*fDim=*/0, /*oDim=*/0, /*idx=*/0)
-      .matchStride(/*iDim=*/1, /*fDim=*/1, /*oDim=*/1, /*idx=*/1)
-      .matchStride(/*iDim=*/2, /*fDim=*/2, /*oDim=*/2, /*idx=*/2)
-      .matchMaps({/*inputMap=*/{m.strided(D, d, 0), m.strided(H, h, 1),
-                                m.strided(W, w, 2)},
-                  /*filterMap=*/{d, h, w},
-                  /*outputMap=*/{D, H, W}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/0, /*fDim=*/0, /*oDim=*/0, /*idx=*/0)
+          .matchStride(/*iDim=*/1, /*fDim=*/1, /*oDim=*/1, /*idx=*/1)
+          .matchStride(/*iDim=*/2, /*fDim=*/2, /*oDim=*/2, /*idx=*/2)
+          .matchMaps({/*inputMap=*/{m.strided(D, d, 0), m.strided(H, h, 1),
+                                    m.strided(W, w, 2)},
+                      /*filterMap=*/{d, h, w},
+                      /*outputMap=*/{D, H, W}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::Conv3DNdhwcDhwcfOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::Conv3DNdhwcDhwcfOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::Conv3DNdhwcDhwcfOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto convOp = dyn_cast<linalg::Conv3DNdhwcDhwcfOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(convOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(convOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/3, dilations, strides);
+  ConvMatcherBuilder m(op, /*spatialRank=*/3, &result.dilations,
+                       &result.strides);
   AffineExpr N = m.dim(0);
   AffineExpr D = m.dim(1);
   AffineExpr H = m.dim(2);
@@ -1056,27 +1173,34 @@ bool isaConvolutionOpOfType<linalg::Conv3DNdhwcDhwcfOp>(
   AffineExpr w = m.dim(7);
   AffineExpr c = m.dim(8);
 
-  return m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
-      .matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/1)
-      .matchStride(/*iDim=*/3, /*fDim=*/2, /*oDim=*/3, /*idx=*/2)
-      .matchMaps({/*inputMap=*/{N, m.strided(D, d, 0), m.strided(H, h, 1),
-                                m.strided(W, w, 2), c},
-                  /*filterMap=*/{d, h, w, c, F},
-                  /*outputMap=*/{N, D, H, W, F}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
+          .matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/1)
+          .matchStride(/*iDim=*/3, /*fDim=*/2, /*oDim=*/3, /*idx=*/2)
+          .matchMaps({/*inputMap=*/{N, m.strided(D, d, 0), m.strided(H, h, 1),
+                                    m.strided(W, w, 2), c},
+                      /*filterMap=*/{d, h, w, c, F},
+                      /*outputMap=*/{N, D, H, W, F}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::Conv3DNdhwcDhwcfQOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::Conv3DNdhwcDhwcfQOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::Conv3DNdhwcDhwcfQOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto convOp = dyn_cast<linalg::Conv3DNdhwcDhwcfQOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(convOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(convOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/3, dilations, strides);
+  ConvMatcherBuilder m(op, /*spatialRank=*/3, &result.dilations,
+                       &result.strides);
   AffineExpr N = m.dim(0);
   AffineExpr D = m.dim(1);
   AffineExpr H = m.dim(2);
@@ -1087,29 +1211,36 @@ bool isaConvolutionOpOfType<linalg::Conv3DNdhwcDhwcfQOp>(
   AffineExpr w = m.dim(7);
   AffineExpr c = m.dim(8);
 
-  return m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
-      .matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/1)
-      .matchStride(/*iDim=*/3, /*fDim=*/2, /*oDim=*/3, /*idx=*/2)
-      .matchMaps({/*inputMap=*/{N, m.strided(D, d, 0), m.strided(H, h, 1),
-                                m.strided(W, w, 2), c},
-                  /*filterMap=*/{d, h, w, c, F},
-                  /*scalarMap=*/{},
-                  /*scalarMap=*/{},
-                  /*outputMap=*/{N, D, H, W, F}})
-      .matchBody(/*containsZeroPointOffset=*/true);
+  if (m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
+          .matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/1)
+          .matchStride(/*iDim=*/3, /*fDim=*/2, /*oDim=*/3, /*idx=*/2)
+          .matchMaps({/*inputMap=*/{N, m.strided(D, d, 0), m.strided(H, h, 1),
+                                    m.strided(W, w, 2), c},
+                      /*filterMap=*/{d, h, w, c, F},
+                      /*scalarMap=*/{},
+                      /*scalarMap=*/{},
+                      /*outputMap=*/{N, D, H, W, F}})
+          .matchBody(/*containsZeroPointOffset=*/true))
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::Conv3DNcdhwFcdhwOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::Conv3DNcdhwFcdhwOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::Conv3DNcdhwFcdhwOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto convOp = dyn_cast<linalg::Conv3DNcdhwFcdhwOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(convOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(convOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/3, dilations, strides);
+  ConvMatcherBuilder m(op, /*spatialRank=*/3, &result.dilations,
+                       &result.strides);
   AffineExpr N = m.dim(0);
   AffineExpr F = m.dim(1);
   AffineExpr D = m.dim(2);
@@ -1120,97 +1251,129 @@ bool isaConvolutionOpOfType<linalg::Conv3DNcdhwFcdhwOp>(
   AffineExpr h = m.dim(7);
   AffineExpr w = m.dim(8);
 
-  return m.matchStride(/*iDim=*/2, /*fDim=*/2, /*oDim=*/2, /*idx=*/0)
-      .matchStride(/*iDim=*/3, /*fDim=*/3, /*oDim=*/3, /*idx=*/1)
-      .matchStride(/*iDim=*/4, /*fDim=*/4, /*oDim=*/4, /*idx=*/2)
-      .matchMaps({/*inputMap=*/{N, c, m.strided(D, d, 0), m.strided(H, h, 1),
-                                m.strided(W, w, 2)},
-                  /*filterMap=*/{F, c, d, h, w},
-                  /*outputMap=*/{N, F, D, H, W}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/2, /*fDim=*/2, /*oDim=*/2, /*idx=*/0)
+          .matchStride(/*iDim=*/3, /*fDim=*/3, /*oDim=*/3, /*idx=*/1)
+          .matchStride(/*iDim=*/4, /*fDim=*/4, /*oDim=*/4, /*idx=*/2)
+          .matchMaps({/*inputMap=*/{N, c, m.strided(D, d, 0),
+                                    m.strided(H, h, 1), m.strided(W, w, 2)},
+                      /*filterMap=*/{F, c, d, h, w},
+                      /*outputMap=*/{N, F, D, H, W}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::DepthwiseConv1DNcwCwOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::DepthwiseConv1DNcwCwOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::DepthwiseConv1DNcwCwOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto convOp =
+          dyn_cast<linalg::DepthwiseConv1DNcwCwOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(convOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(convOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/1, dilations, strides);
+  ConvMatcherBuilder m(op, /*spatialRank=*/1, &result.dilations,
+                       &result.strides);
   AffineExpr N = m.dim(0);
   AffineExpr W = m.dim(1);
   AffineExpr C = m.dim(2);
   AffineExpr w = m.dim(3);
 
-  return m.matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/0)
-      .matchMaps({/*inputMap=*/{N, C, m.strided(W, w, 0)},
-                  /*filterMap=*/{C, w},
-                  /*outputMap=*/{N, C, W}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/0)
+          .matchMaps({/*inputMap=*/{N, C, m.strided(W, w, 0)},
+                      /*filterMap=*/{C, w},
+                      /*outputMap=*/{N, C, W}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::DepthwiseConv1DNwcWcOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::DepthwiseConv1DNwcWcOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::DepthwiseConv1DNwcWcOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto convOp =
+          dyn_cast<linalg::DepthwiseConv1DNwcWcOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(convOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(convOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/1, dilations, strides);
+  ConvMatcherBuilder m(op, /*spatialRank=*/1, &result.dilations,
+                       &result.strides);
   AffineExpr N = m.dim(0);
   AffineExpr W = m.dim(1);
   AffineExpr C = m.dim(2);
   AffineExpr w = m.dim(3);
 
-  return m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
-      .matchMaps({/*inputMap=*/{N, m.strided(W, w, 0), C},
-                  /*filterMap=*/{w, C},
-                  /*outputMap=*/{N, W, C}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
+          .matchMaps({/*inputMap=*/{N, m.strided(W, w, 0), C},
+                      /*filterMap=*/{w, C},
+                      /*outputMap=*/{N, W, C}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::DepthwiseConv1DNwcWcmOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::DepthwiseConv1DNwcWcmOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::DepthwiseConv1DNwcWcmOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto convOp =
+          dyn_cast<linalg::DepthwiseConv1DNwcWcmOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(convOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(convOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/1, dilations, strides);
+  ConvMatcherBuilder m(op, /*spatialRank=*/1, &result.dilations,
+                       &result.strides);
   AffineExpr N = m.dim(0);
   AffineExpr W = m.dim(1);
   AffineExpr C = m.dim(2);
   AffineExpr CM = m.dim(3);
   AffineExpr w = m.dim(4);
 
-  return m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
-      .matchMaps({/*inputMap=*/{N, m.strided(W, w, 0), C},
-                  /*filterMap=*/{w, C, CM},
-                  /*outputMap=*/{N, W, C, CM}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
+          .matchMaps({/*inputMap=*/{N, m.strided(W, w, 0), C},
+                      /*filterMap=*/{w, C, CM},
+                      /*outputMap=*/{N, W, C, CM}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::DepthwiseConv2DNchwChwOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::DepthwiseConv2DNchwChwOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::DepthwiseConv2DNchwChwOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto convOp =
+          dyn_cast<linalg::DepthwiseConv2DNchwChwOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(convOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(convOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/2, dilations, strides);
+  ConvMatcherBuilder m(op, /*spatialRank=*/2, &result.dilations,
+                       &result.strides);
   AffineExpr N = m.dim(0);
   AffineExpr H = m.dim(1);
   AffineExpr W = m.dim(2);
@@ -1218,25 +1381,34 @@ bool isaConvolutionOpOfType<linalg::DepthwiseConv2DNchwChwOp>(
   AffineExpr h = m.dim(4);
   AffineExpr w = m.dim(5);
 
-  return m.matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/0)
-      .matchStride(/*iDim=*/3, /*fDim=*/2, /*oDim=*/3, /*idx=*/1)
-      .matchMaps({/*inputMap=*/{N, C, m.strided(H, h, 0), m.strided(W, w, 1)},
-                  /*filterMap=*/{C, h, w},
-                  /*outputMap=*/{N, C, H, W}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/0)
+          .matchStride(/*iDim=*/3, /*fDim=*/2, /*oDim=*/3, /*idx=*/1)
+          .matchMaps(
+              {/*inputMap=*/{N, C, m.strided(H, h, 0), m.strided(W, w, 1)},
+               /*filterMap=*/{C, h, w},
+               /*outputMap=*/{N, C, H, W}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::DepthwiseConv2DNhwcHwcOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::DepthwiseConv2DNhwcHwcOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::DepthwiseConv2DNhwcHwcOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto convOp =
+          dyn_cast<linalg::DepthwiseConv2DNhwcHwcOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(convOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(convOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/2, dilations, strides);
+  ConvMatcherBuilder m(op, /*spatialRank=*/2, &result.dilations,
+                       &result.strides);
   AffineExpr N = m.dim(0);
   AffineExpr H = m.dim(1);
   AffineExpr W = m.dim(2);
@@ -1244,25 +1416,34 @@ bool isaConvolutionOpOfType<linalg::DepthwiseConv2DNhwcHwcOp>(
   AffineExpr h = m.dim(4);
   AffineExpr w = m.dim(5);
 
-  return m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
-      .matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/1)
-      .matchMaps({/*inputMap=*/{N, m.strided(H, h, 0), m.strided(W, w, 1), C},
-                  /*filterMap=*/{h, w, C},
-                  /*outputMap=*/{N, H, W, C}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
+          .matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/1)
+          .matchMaps(
+              {/*inputMap=*/{N, m.strided(H, h, 0), m.strided(W, w, 1), C},
+               /*filterMap=*/{h, w, C},
+               /*outputMap=*/{N, H, W, C}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::DepthwiseConv2DNhwcHwcQOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::DepthwiseConv2DNhwcHwcQOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::DepthwiseConv2DNhwcHwcQOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto convOp =
+          dyn_cast<linalg::DepthwiseConv2DNhwcHwcQOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(convOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(convOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/2, dilations, strides);
+  ConvMatcherBuilder m(op, /*spatialRank=*/2, &result.dilations,
+                       &result.strides);
   AffineExpr N = m.dim(0);
   AffineExpr H = m.dim(1);
   AffineExpr W = m.dim(2);
@@ -1270,27 +1451,36 @@ bool isaConvolutionOpOfType<linalg::DepthwiseConv2DNhwcHwcQOp>(
   AffineExpr h = m.dim(4);
   AffineExpr w = m.dim(5);
 
-  return m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
-      .matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/1)
-      .matchMaps({/*inputMap=*/{N, m.strided(H, h, 0), m.strided(W, w, 1), C},
-                  /*filterMap=*/{h, w, C},
-                  /*scalarMap=*/{},
-                  /*scalarMap=*/{},
-                  /*outputMap=*/{N, H, W, C}})
-      .matchBody(/*containsZeroPointOffset=*/true);
+  if (m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
+          .matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/1)
+          .matchMaps(
+              {/*inputMap=*/{N, m.strided(H, h, 0), m.strided(W, w, 1), C},
+               /*filterMap=*/{h, w, C},
+               /*scalarMap=*/{},
+               /*scalarMap=*/{},
+               /*outputMap=*/{N, H, W, C}})
+          .matchBody(/*containsZeroPointOffset=*/true))
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::DepthwiseConv2DNhwcHwcmOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::DepthwiseConv2DNhwcHwcmOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::DepthwiseConv2DNhwcHwcmOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto convOp =
+          dyn_cast<linalg::DepthwiseConv2DNhwcHwcmOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(convOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(convOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/2, dilations, strides);
+  ConvMatcherBuilder m(op, /*spatialRank=*/2, &result.dilations,
+                       &result.strides);
   AffineExpr N = m.dim(0);
   AffineExpr H = m.dim(1);
   AffineExpr W = m.dim(2);
@@ -1299,25 +1489,34 @@ bool isaConvolutionOpOfType<linalg::DepthwiseConv2DNhwcHwcmOp>(
   AffineExpr h = m.dim(5);
   AffineExpr w = m.dim(6);
 
-  return m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
-      .matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/1)
-      .matchMaps({/*inputMap=*/{N, m.strided(H, h, 0), m.strided(W, w, 1), C},
-                  /*filterMap=*/{h, w, C, CM},
-                  /*outputMap=*/{N, H, W, C, CM}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
+          .matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/1)
+          .matchMaps(
+              {/*inputMap=*/{N, m.strided(H, h, 0), m.strided(W, w, 1), C},
+               /*filterMap=*/{h, w, C, CM},
+               /*outputMap=*/{N, H, W, C, CM}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::DepthwiseConv2DNhwcHwcmQOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::DepthwiseConv2DNhwcHwcmQOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::DepthwiseConv2DNhwcHwcmQOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto convOp =
+          dyn_cast<linalg::DepthwiseConv2DNhwcHwcmQOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(convOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(convOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/2, dilations, strides);
+  ConvMatcherBuilder m(op, /*spatialRank=*/2, &result.dilations,
+                       &result.strides);
   AffineExpr N = m.dim(0);
   AffineExpr H = m.dim(1);
   AffineExpr W = m.dim(2);
@@ -1326,27 +1525,36 @@ bool isaConvolutionOpOfType<linalg::DepthwiseConv2DNhwcHwcmQOp>(
   AffineExpr h = m.dim(5);
   AffineExpr w = m.dim(6);
 
-  return m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
-      .matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/1)
-      .matchMaps({/*inputMap=*/{N, m.strided(H, h, 0), m.strided(W, w, 1), C},
-                  /*filterMap=*/{h, w, C, CM},
-                  /*scalarMap=*/{},
-                  /*scalarMap=*/{},
-                  /*outputMap=*/{N, H, W, C, CM}})
-      .matchBody(/*containsZeroPointOffset=*/true);
+  if (m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
+          .matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/1)
+          .matchMaps(
+              {/*inputMap=*/{N, m.strided(H, h, 0), m.strided(W, w, 1), C},
+               /*filterMap=*/{h, w, C, CM},
+               /*scalarMap=*/{},
+               /*scalarMap=*/{},
+               /*outputMap=*/{N, H, W, C, CM}})
+          .matchBody(/*containsZeroPointOffset=*/true))
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::DepthwiseConv3DNdhwcDhwcOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::DepthwiseConv3DNdhwcDhwcOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::DepthwiseConv3DNdhwcDhwcOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto convOp =
+          dyn_cast<linalg::DepthwiseConv3DNdhwcDhwcOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(convOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(convOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/3, dilations, strides);
+  ConvMatcherBuilder m(op, /*spatialRank=*/3, &result.dilations,
+                       &result.strides);
   AffineExpr N = m.dim(0);
   AffineExpr D = m.dim(1);
   AffineExpr H = m.dim(2);
@@ -1356,27 +1564,35 @@ bool isaConvolutionOpOfType<linalg::DepthwiseConv3DNdhwcDhwcOp>(
   AffineExpr w = m.dim(6);
   AffineExpr C = m.dim(7);
 
-  return m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
-      .matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/1)
-      .matchStride(/*iDim=*/3, /*fDim=*/2, /*oDim=*/3, /*idx=*/2)
-      .matchMaps({/*inputMap=*/{N, m.strided(D, d, 0), m.strided(H, h, 1),
-                                m.strided(W, w, 2), C},
-                  /*filterMap=*/{d, h, w, C},
-                  /*outputMap=*/{N, D, H, W, C}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
+          .matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/1)
+          .matchStride(/*iDim=*/3, /*fDim=*/2, /*oDim=*/3, /*idx=*/2)
+          .matchMaps({/*inputMap=*/{N, m.strided(D, d, 0), m.strided(H, h, 1),
+                                    m.strided(W, w, 2), C},
+                      /*filterMap=*/{d, h, w, C},
+                      /*outputMap=*/{N, D, H, W, C}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::DepthwiseConv3DNcdhwCdhwOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::DepthwiseConv3DNcdhwCdhwOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::DepthwiseConv3DNcdhwCdhwOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto convOp =
+          dyn_cast<linalg::DepthwiseConv3DNcdhwCdhwOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(convOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(convOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/3, dilations, strides);
+  ConvMatcherBuilder m(op, /*spatialRank=*/3, &result.dilations,
+                       &result.strides);
   AffineExpr N = m.dim(0);
   AffineExpr D = m.dim(1);
   AffineExpr H = m.dim(2);
@@ -1386,27 +1602,35 @@ bool isaConvolutionOpOfType<linalg::DepthwiseConv3DNcdhwCdhwOp>(
   AffineExpr w = m.dim(6);
   AffineExpr C = m.dim(7);
 
-  return m.matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/0)
-      .matchStride(/*iDim=*/3, /*fDim=*/2, /*oDim=*/3, /*idx=*/1)
-      .matchStride(/*iDim=*/4, /*fDim=*/3, /*oDim=*/4, /*idx=*/2)
-      .matchMaps({/*inputMap=*/{N, C, m.strided(D, d, 0), m.strided(H, h, 1),
-                                m.strided(W, w, 2)},
-                  /*filterMap=*/{C, d, h, w},
-                  /*outputMap=*/{N, C, D, H, W}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/0)
+          .matchStride(/*iDim=*/3, /*fDim=*/2, /*oDim=*/3, /*idx=*/1)
+          .matchStride(/*iDim=*/4, /*fDim=*/3, /*oDim=*/4, /*idx=*/2)
+          .matchMaps({/*inputMap=*/{N, C, m.strided(D, d, 0),
+                                    m.strided(H, h, 1), m.strided(W, w, 2)},
+                      /*filterMap=*/{C, d, h, w},
+                      /*outputMap=*/{N, C, D, H, W}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::DepthwiseConv3DNdhwcDhwcmOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::DepthwiseConv3DNdhwcDhwcmOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::DepthwiseConv3DNdhwcDhwcmOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto convOp =
+          dyn_cast<linalg::DepthwiseConv3DNdhwcDhwcmOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(convOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(convOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/3, dilations, strides);
+  ConvMatcherBuilder m(op, /*spatialRank=*/3, &result.dilations,
+                       &result.strides);
   AffineExpr N = m.dim(0);
   AffineExpr D = m.dim(1);
   AffineExpr H = m.dim(2);
@@ -1417,28 +1641,34 @@ bool isaConvolutionOpOfType<linalg::DepthwiseConv3DNdhwcDhwcmOp>(
   AffineExpr w = m.dim(7);
   AffineExpr C = m.dim(8);
 
-  return m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
-      .matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/1)
-      .matchStride(/*iDim=*/3, /*fDim=*/2, /*oDim=*/3, /*idx=*/2)
-      .matchMaps({/*inputMap=*/{N, m.strided(D, d, 0), m.strided(H, h, 1),
-                                m.strided(W, w, 2), C},
-                  /*filterMap=*/{d, h, w, C, CM},
-                  /*outputMap=*/{N, D, H, W, C, CM}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
+          .matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/1)
+          .matchStride(/*iDim=*/3, /*fDim=*/2, /*oDim=*/3, /*idx=*/2)
+          .matchMaps({/*inputMap=*/{N, m.strided(D, d, 0), m.strided(H, h, 1),
+                                    m.strided(W, w, 2), C},
+                      /*filterMap=*/{d, h, w, C, CM},
+                      /*outputMap=*/{N, D, H, W, C, CM}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::PoolingNhwcMaxOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::PoolingNhwcMaxOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::PoolingNhwcMaxOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto poolOp = dyn_cast<linalg::PoolingNhwcMaxOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(poolOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(poolOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/2, dilations, strides,
-                       PoolingType::MaxSigned);
+  ConvMatcherBuilder m(op, /*spatialRank=*/2, &result.dilations,
+                       &result.strides, PoolingType::MaxSigned);
   AffineExpr N = m.dim(0);
   AffineExpr H = m.dim(1);
   AffineExpr W = m.dim(2);
@@ -1446,26 +1676,33 @@ bool isaConvolutionOpOfType<linalg::PoolingNhwcMaxOp>(
   AffineExpr h = m.dim(4);
   AffineExpr w = m.dim(5);
 
-  return m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
-      .matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/1)
-      .matchMaps({/*inputMap=*/{N, m.strided(H, h, 0), m.strided(W, w, 1), C},
-                  /*filterMap=*/{h, w},
-                  /*outputMap=*/{N, H, W, C}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
+          .matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/1)
+          .matchMaps(
+              {/*inputMap=*/{N, m.strided(H, h, 0), m.strided(W, w, 1), C},
+               /*filterMap=*/{h, w},
+               /*outputMap=*/{N, H, W, C}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::PoolingNhwcMinOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::PoolingNhwcMinOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::PoolingNhwcMinOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto poolOp = dyn_cast<linalg::PoolingNhwcMinOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(poolOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(poolOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/2, dilations, strides,
-                       PoolingType::MinSigned);
+  ConvMatcherBuilder m(op, /*spatialRank=*/2, &result.dilations,
+                       &result.strides, PoolingType::MinSigned);
   AffineExpr N = m.dim(0);
   AffineExpr H = m.dim(1);
   AffineExpr W = m.dim(2);
@@ -1473,26 +1710,33 @@ bool isaConvolutionOpOfType<linalg::PoolingNhwcMinOp>(
   AffineExpr h = m.dim(4);
   AffineExpr w = m.dim(5);
 
-  return m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
-      .matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/1)
-      .matchMaps({/*inputMap=*/{N, m.strided(H, h, 0), m.strided(W, w, 1), C},
-                  /*filterMap=*/{h, w},
-                  /*outputMap=*/{N, H, W, C}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
+          .matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/1)
+          .matchMaps(
+              {/*inputMap=*/{N, m.strided(H, h, 0), m.strided(W, w, 1), C},
+               /*filterMap=*/{h, w},
+               /*outputMap=*/{N, H, W, C}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::PoolingNhwcSumOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::PoolingNhwcSumOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::PoolingNhwcSumOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto poolOp = dyn_cast<linalg::PoolingNhwcSumOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(poolOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(poolOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/2, dilations, strides,
-                       PoolingType::Sum);
+  ConvMatcherBuilder m(op, /*spatialRank=*/2, &result.dilations,
+                       &result.strides, PoolingType::Sum);
   AffineExpr N = m.dim(0);
   AffineExpr H = m.dim(1);
   AffineExpr W = m.dim(2);
@@ -1500,26 +1744,34 @@ bool isaConvolutionOpOfType<linalg::PoolingNhwcSumOp>(
   AffineExpr h = m.dim(4);
   AffineExpr w = m.dim(5);
 
-  return m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
-      .matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/1)
-      .matchMaps({/*inputMap=*/{N, m.strided(H, h, 0), m.strided(W, w, 1), C},
-                  /*filterMap=*/{h, w},
-                  /*outputMap=*/{N, H, W, C}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
+          .matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/1)
+          .matchMaps(
+              {/*inputMap=*/{N, m.strided(H, h, 0), m.strided(W, w, 1), C},
+               /*filterMap=*/{h, w},
+               /*outputMap=*/{N, H, W, C}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::PoolingNhwcMaxUnsignedOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::PoolingNhwcMaxUnsignedOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::PoolingNhwcMaxUnsignedOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto poolOp =
+          dyn_cast<linalg::PoolingNhwcMaxUnsignedOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(poolOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(poolOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/2, dilations, strides,
-                       PoolingType::MaxUnsigned);
+  ConvMatcherBuilder m(op, /*spatialRank=*/2, &result.dilations,
+                       &result.strides, PoolingType::MaxUnsigned);
   AffineExpr N = m.dim(0);
   AffineExpr H = m.dim(1);
   AffineExpr W = m.dim(2);
@@ -1527,26 +1779,34 @@ bool isaConvolutionOpOfType<linalg::PoolingNhwcMaxUnsignedOp>(
   AffineExpr h = m.dim(4);
   AffineExpr w = m.dim(5);
 
-  return m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
-      .matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/1)
-      .matchMaps({/*inputMap=*/{N, m.strided(H, h, 0), m.strided(W, w, 1), C},
-                  /*filterMap=*/{h, w},
-                  /*outputMap=*/{N, H, W, C}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
+          .matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/1)
+          .matchMaps(
+              {/*inputMap=*/{N, m.strided(H, h, 0), m.strided(W, w, 1), C},
+               /*filterMap=*/{h, w},
+               /*outputMap=*/{N, H, W, C}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::PoolingNhwcMinUnsignedOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::PoolingNhwcMinUnsignedOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::PoolingNhwcMinUnsignedOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto poolOp =
+          dyn_cast<linalg::PoolingNhwcMinUnsignedOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(poolOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(poolOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/2, dilations, strides,
-                       PoolingType::MinUnsigned);
+  ConvMatcherBuilder m(op, /*spatialRank=*/2, &result.dilations,
+                       &result.strides, PoolingType::MinUnsigned);
   AffineExpr N = m.dim(0);
   AffineExpr H = m.dim(1);
   AffineExpr W = m.dim(2);
@@ -1554,26 +1814,33 @@ bool isaConvolutionOpOfType<linalg::PoolingNhwcMinUnsignedOp>(
   AffineExpr h = m.dim(4);
   AffineExpr w = m.dim(5);
 
-  return m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
-      .matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/1)
-      .matchMaps({/*inputMap=*/{N, m.strided(H, h, 0), m.strided(W, w, 1), C},
-                  /*filterMap=*/{h, w},
-                  /*outputMap=*/{N, H, W, C}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
+          .matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/1)
+          .matchMaps(
+              {/*inputMap=*/{N, m.strided(H, h, 0), m.strided(W, w, 1), C},
+               /*filterMap=*/{h, w},
+               /*outputMap=*/{N, H, W, C}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::PoolingNchwSumOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::PoolingNchwSumOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::PoolingNchwSumOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto poolOp = dyn_cast<linalg::PoolingNchwSumOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(poolOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(poolOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/2, dilations, strides,
-                       PoolingType::Sum);
+  ConvMatcherBuilder m(op, /*spatialRank=*/2, &result.dilations,
+                       &result.strides, PoolingType::Sum);
   AffineExpr N = m.dim(0);
   AffineExpr C = m.dim(1);
   AffineExpr H = m.dim(2);
@@ -1581,26 +1848,33 @@ bool isaConvolutionOpOfType<linalg::PoolingNchwSumOp>(
   AffineExpr h = m.dim(4);
   AffineExpr w = m.dim(5);
 
-  return m.matchStride(/*iDim=*/2, /*fDim=*/0, /*oDim=*/2, /*idx=*/0)
-      .matchStride(/*iDim=*/3, /*fDim=*/1, /*oDim=*/3, /*idx=*/1)
-      .matchMaps({/*inputMap=*/{N, C, m.strided(H, h, 0), m.strided(W, w, 1)},
-                  /*filterMap=*/{h, w},
-                  /*outputMap=*/{N, C, H, W}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/2, /*fDim=*/0, /*oDim=*/2, /*idx=*/0)
+          .matchStride(/*iDim=*/3, /*fDim=*/1, /*oDim=*/3, /*idx=*/1)
+          .matchMaps(
+              {/*inputMap=*/{N, C, m.strided(H, h, 0), m.strided(W, w, 1)},
+               /*filterMap=*/{h, w},
+               /*outputMap=*/{N, C, H, W}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::PoolingNchwMaxOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::PoolingNchwMaxOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::PoolingNchwMaxOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto poolOp = dyn_cast<linalg::PoolingNchwMaxOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(poolOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(poolOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/2, dilations, strides,
-                       PoolingType::MaxSigned);
+  ConvMatcherBuilder m(op, /*spatialRank=*/2, &result.dilations,
+                       &result.strides, PoolingType::MaxSigned);
   AffineExpr N = m.dim(0);
   AffineExpr C = m.dim(1);
   AffineExpr H = m.dim(2);
@@ -1608,194 +1882,245 @@ bool isaConvolutionOpOfType<linalg::PoolingNchwMaxOp>(
   AffineExpr h = m.dim(4);
   AffineExpr w = m.dim(5);
 
-  return m.matchStride(/*iDim=*/2, /*fDim=*/0, /*oDim=*/2, /*idx=*/0)
-      .matchStride(/*iDim=*/3, /*fDim=*/1, /*oDim=*/3, /*idx=*/1)
-      .matchMaps({/*inputMap=*/{N, C, m.strided(H, h, 0), m.strided(W, w, 1)},
-                  /*filterMap=*/{h, w},
-                  /*outputMap=*/{N, C, H, W}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/2, /*fDim=*/0, /*oDim=*/2, /*idx=*/0)
+          .matchStride(/*iDim=*/3, /*fDim=*/1, /*oDim=*/3, /*idx=*/1)
+          .matchMaps(
+              {/*inputMap=*/{N, C, m.strided(H, h, 0), m.strided(W, w, 1)},
+               /*filterMap=*/{h, w},
+               /*outputMap=*/{N, C, H, W}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::PoolingNwcSumOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::PoolingNwcSumOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::PoolingNwcSumOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto poolOp = dyn_cast<linalg::PoolingNwcSumOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(poolOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(poolOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/1, dilations, strides,
-                       PoolingType::Sum);
+  ConvMatcherBuilder m(op, /*spatialRank=*/1, &result.dilations,
+                       &result.strides, PoolingType::Sum);
   AffineExpr N = m.dim(0);
   AffineExpr W = m.dim(1);
   AffineExpr C = m.dim(2);
   AffineExpr w = m.dim(3);
 
-  return m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
-      .matchMaps({/*inputMap=*/{N, m.strided(W, w, 0), C},
-                  /*filterMap=*/{w},
-                  /*outputMap=*/{N, W, C}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
+          .matchMaps({/*inputMap=*/{N, m.strided(W, w, 0), C},
+                      /*filterMap=*/{w},
+                      /*outputMap=*/{N, W, C}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::PoolingNcwSumOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::PoolingNcwSumOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::PoolingNcwSumOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto poolOp = dyn_cast<linalg::PoolingNcwSumOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(poolOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(poolOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/1, dilations, strides,
-                       PoolingType::Sum);
+  ConvMatcherBuilder m(op, /*spatialRank=*/1, &result.dilations,
+                       &result.strides, PoolingType::Sum);
   AffineExpr N = m.dim(0);
   AffineExpr C = m.dim(1);
   AffineExpr W = m.dim(2);
   AffineExpr w = m.dim(3);
 
-  return m.matchStride(/*iDim=*/2, /*fDim=*/0, /*oDim=*/2, /*idx=*/0)
-      .matchMaps({/*inputMap=*/{N, C, m.strided(W, w, 0)},
-                  /*filterMap=*/{w},
-                  /*outputMap=*/{N, C, W}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/2, /*fDim=*/0, /*oDim=*/2, /*idx=*/0)
+          .matchMaps({/*inputMap=*/{N, C, m.strided(W, w, 0)},
+                      /*filterMap=*/{w},
+                      /*outputMap=*/{N, C, W}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::PoolingNwcMaxOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::PoolingNwcMaxOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::PoolingNwcMaxOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto poolOp = dyn_cast<linalg::PoolingNwcMaxOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(poolOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(poolOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/1, dilations, strides,
-                       PoolingType::MaxSigned);
+  ConvMatcherBuilder m(op, /*spatialRank=*/1, &result.dilations,
+                       &result.strides, PoolingType::MaxSigned);
   AffineExpr N = m.dim(0);
   AffineExpr W = m.dim(1);
   AffineExpr C = m.dim(2);
   AffineExpr w = m.dim(3);
 
-  return m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
-      .matchMaps({/*inputMap=*/{N, m.strided(W, w, 0), C},
-                  /*filterMap=*/{w},
-                  /*outputMap=*/{N, W, C}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
+          .matchMaps({/*inputMap=*/{N, m.strided(W, w, 0), C},
+                      /*filterMap=*/{w},
+                      /*outputMap=*/{N, W, C}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::PoolingNwcMaxUnsignedOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::PoolingNwcMaxUnsignedOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::PoolingNwcMaxUnsignedOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto poolOp =
+          dyn_cast<linalg::PoolingNwcMaxUnsignedOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(poolOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(poolOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/1, dilations, strides,
-                       PoolingType::MaxUnsigned);
+  ConvMatcherBuilder m(op, /*spatialRank=*/1, &result.dilations,
+                       &result.strides, PoolingType::MaxUnsigned);
   AffineExpr N = m.dim(0);
   AffineExpr W = m.dim(1);
   AffineExpr C = m.dim(2);
   AffineExpr w = m.dim(3);
 
-  return m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
-      .matchMaps({/*inputMap=*/{N, m.strided(W, w, 0), C},
-                  /*filterMap=*/{w},
-                  /*outputMap=*/{N, W, C}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
+          .matchMaps({/*inputMap=*/{N, m.strided(W, w, 0), C},
+                      /*filterMap=*/{w},
+                      /*outputMap=*/{N, W, C}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::PoolingNcwMaxOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::PoolingNcwMaxOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::PoolingNcwMaxOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto poolOp = dyn_cast<linalg::PoolingNcwMaxOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(poolOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(poolOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/1, dilations, strides,
-                       PoolingType::MaxSigned);
+  ConvMatcherBuilder m(op, /*spatialRank=*/1, &result.dilations,
+                       &result.strides, PoolingType::MaxSigned);
   AffineExpr N = m.dim(0);
   AffineExpr C = m.dim(1);
   AffineExpr W = m.dim(2);
   AffineExpr w = m.dim(3);
 
-  return m.matchStride(/*iDim=*/2, /*fDim=*/0, /*oDim=*/2, /*idx=*/0)
-      .matchMaps({/*inputMap=*/{N, C, m.strided(W, w, 0)},
-                  /*filterMap=*/{w},
-                  /*outputMap=*/{N, C, W}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/2, /*fDim=*/0, /*oDim=*/2, /*idx=*/0)
+          .matchMaps({/*inputMap=*/{N, C, m.strided(W, w, 0)},
+                      /*filterMap=*/{w},
+                      /*outputMap=*/{N, C, W}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::PoolingNwcMinOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::PoolingNwcMinOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::PoolingNwcMinOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto poolOp = dyn_cast<linalg::PoolingNwcMinOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(poolOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(poolOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/1, dilations, strides,
-                       PoolingType::MinSigned);
+  ConvMatcherBuilder m(op, /*spatialRank=*/1, &result.dilations,
+                       &result.strides, PoolingType::MinSigned);
   AffineExpr N = m.dim(0);
   AffineExpr W = m.dim(1);
   AffineExpr C = m.dim(2);
   AffineExpr w = m.dim(3);
 
-  return m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
-      .matchMaps({/*inputMap=*/{N, m.strided(W, w, 0), C},
-                  /*filterMap=*/{w},
-                  /*outputMap=*/{N, W, C}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
+          .matchMaps({/*inputMap=*/{N, m.strided(W, w, 0), C},
+                      /*filterMap=*/{w},
+                      /*outputMap=*/{N, W, C}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::PoolingNwcMinUnsignedOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::PoolingNwcMinUnsignedOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::PoolingNwcMinUnsignedOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto poolOp =
+          dyn_cast<linalg::PoolingNwcMinUnsignedOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(poolOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(poolOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/1, dilations, strides,
-                       PoolingType::MinUnsigned);
+  ConvMatcherBuilder m(op, /*spatialRank=*/1, &result.dilations,
+                       &result.strides, PoolingType::MinUnsigned);
   AffineExpr N = m.dim(0);
   AffineExpr W = m.dim(1);
   AffineExpr C = m.dim(2);
   AffineExpr w = m.dim(3);
 
-  return m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
-      .matchMaps({/*inputMap=*/{N, m.strided(W, w, 0), C},
-                  /*filterMap=*/{w},
-                  /*outputMap=*/{N, W, C}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
+          .matchMaps({/*inputMap=*/{N, m.strided(W, w, 0), C},
+                      /*filterMap=*/{w},
+                      /*outputMap=*/{N, W, C}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::PoolingNdhwcSumOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::PoolingNdhwcSumOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::PoolingNdhwcSumOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto poolOp = dyn_cast<linalg::PoolingNdhwcSumOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(poolOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(poolOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/3, dilations, strides,
-                       PoolingType::Sum);
+  ConvMatcherBuilder m(op, /*spatialRank=*/3, &result.dilations,
+                       &result.strides, PoolingType::Sum);
   AffineExpr N = m.dim(0);
   AffineExpr D = m.dim(1);
   AffineExpr H = m.dim(2);
@@ -1805,28 +2130,34 @@ bool isaConvolutionOpOfType<linalg::PoolingNdhwcSumOp>(
   AffineExpr h = m.dim(6);
   AffineExpr w = m.dim(7);
 
-  return m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
-      .matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/1)
-      .matchStride(/*iDim=*/3, /*fDim=*/2, /*oDim=*/3, /*idx=*/2)
-      .matchMaps({/*inputMap=*/{N, m.strided(D, d, 0), m.strided(H, h, 1),
-                                m.strided(W, w, 2), C},
-                  /*filterMap=*/{d, h, w},
-                  /*outputMap=*/{N, D, H, W, C}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
+          .matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/1)
+          .matchStride(/*iDim=*/3, /*fDim=*/2, /*oDim=*/3, /*idx=*/2)
+          .matchMaps({/*inputMap=*/{N, m.strided(D, d, 0), m.strided(H, h, 1),
+                                    m.strided(W, w, 2), C},
+                      /*filterMap=*/{d, h, w},
+                      /*outputMap=*/{N, D, H, W, C}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::PoolingNdhwcMaxOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::PoolingNdhwcMaxOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::PoolingNdhwcMaxOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto poolOp = dyn_cast<linalg::PoolingNdhwcMaxOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(poolOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(poolOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/3, dilations, strides,
-                       PoolingType::MaxSigned);
+  ConvMatcherBuilder m(op, /*spatialRank=*/3, &result.dilations,
+                       &result.strides, PoolingType::MaxSigned);
   AffineExpr N = m.dim(0);
   AffineExpr D = m.dim(1);
   AffineExpr H = m.dim(2);
@@ -1836,28 +2167,34 @@ bool isaConvolutionOpOfType<linalg::PoolingNdhwcMaxOp>(
   AffineExpr h = m.dim(6);
   AffineExpr w = m.dim(7);
 
-  return m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
-      .matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/1)
-      .matchStride(/*iDim=*/3, /*fDim=*/2, /*oDim=*/3, /*idx=*/2)
-      .matchMaps({/*inputMap=*/{N, m.strided(D, d, 0), m.strided(H, h, 1),
-                                m.strided(W, w, 2), C},
-                  /*filterMap=*/{d, h, w},
-                  /*outputMap=*/{N, D, H, W, C}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
+          .matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/1)
+          .matchStride(/*iDim=*/3, /*fDim=*/2, /*oDim=*/3, /*idx=*/2)
+          .matchMaps({/*inputMap=*/{N, m.strided(D, d, 0), m.strided(H, h, 1),
+                                    m.strided(W, w, 2), C},
+                      /*filterMap=*/{d, h, w},
+                      /*outputMap=*/{N, D, H, W, C}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 template <>
-bool isaConvolutionOpOfType<linalg::PoolingNdhwcMinOp>(
-    LinalgOp op, SmallVector<int64_t> *dilations,
-    SmallVector<int64_t> *strides) {
-  if (isa<linalg::PoolingNdhwcMinOp>(op))
-    return true;
+std::optional<DilationsAndStrides>
+matchConvolutionOpOfType<linalg::PoolingNdhwcMinOp>(LinalgOp op) {
+  DilationsAndStrides result;
+  if (auto poolOp = dyn_cast<linalg::PoolingNdhwcMinOp>(op.getOperation())) {
+    result.dilations =
+        llvm::to_vector(poolOp.getDilations().getValues<int64_t>());
+    result.strides = llvm::to_vector(poolOp.getStrides().getValues<int64_t>());
+    return result;
+  }
 
   assert(isaConvolutionOpInterface(op) &&
          "expected op to implement ConvolutionOpInterface");
 
-  ConvMatcherBuilder m(op, /*spatialRank=*/3, dilations, strides,
-                       PoolingType::MinSigned);
+  ConvMatcherBuilder m(op, /*spatialRank=*/3, &result.dilations,
+                       &result.strides, PoolingType::MinSigned);
   AffineExpr N = m.dim(0);
   AffineExpr D = m.dim(1);
   AffineExpr H = m.dim(2);
@@ -1867,14 +2204,16 @@ bool isaConvolutionOpOfType<linalg::PoolingNdhwcMinOp>(
   AffineExpr h = m.dim(6);
   AffineExpr w = m.dim(7);
 
-  return m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
-      .matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/1)
-      .matchStride(/*iDim=*/3, /*fDim=*/2, /*oDim=*/3, /*idx=*/2)
-      .matchMaps({/*inputMap=*/{N, m.strided(D, d, 0), m.strided(H, h, 1),
-                                m.strided(W, w, 2), C},
-                  /*filterMap=*/{d, h, w},
-                  /*outputMap=*/{N, D, H, W, C}})
-      .matchBody();
+  if (m.matchStride(/*iDim=*/1, /*fDim=*/0, /*oDim=*/1, /*idx=*/0)
+          .matchStride(/*iDim=*/2, /*fDim=*/1, /*oDim=*/2, /*idx=*/1)
+          .matchStride(/*iDim=*/3, /*fDim=*/2, /*oDim=*/3, /*idx=*/2)
+          .matchMaps({/*inputMap=*/{N, m.strided(D, d, 0), m.strided(H, h, 1),
+                                    m.strided(W, w, 2), C},
+                      /*filterMap=*/{d, h, w},
+                      /*outputMap=*/{N, D, H, W, C}})
+          .matchBody())
+    return result;
+  return std::nullopt;
 }
 
 Value makeComposedPadHighOp(OpBuilder &b, Location loc, RankedTensorType type,


        


More information about the Mlir-commits mailing list