[Mlir-commits] [mlir] [mlir][vector] Add an eliminate-vector-masks pass (PR #226517)

Dhairyashil R G llvmlistbot at llvm.org
Fri Sep 25 08:06:10 PDT 2026


https://github.com/dhairyashilRG created https://github.com/llvm/llvm-project/pull/226517

`eliminateVectorMasks` had no in-tree caller other than a test pass, so no pipeline could use it. This exposes it as an opt-in `-eliminate-vector-masks` pass and drops the test pass.

The pass rewrites `vector.create_mask` ops that can be proven all-true into `vector.constant_mask`; canonicalization then folds those away, so a masked transfer becomes an unmasked one. Mask sizes are bounded with `ValueBoundsOpInterface`, so a loop-derived size like `%dim - %iv` can be proven.

Scalable dimensions need a `vscale` range, given by `vscale-min`/`vscale-max`. Leaving them unset means fixed-size reasoning only; a half-specified or inverted range is rejected rather than silently ignored.

`eliminate-masks.mlir` keeps its checks and only switches its two RUN lines, which shows the pass behaves as the test pass did.

Follow-up to #221595, and the pass promised when #215340 was closed.

Context: https://discourse.llvm.org/t/rfc-should-vector-transfer-read-write-keep-in-bounds-measurements-on-the-masking-alternative/91649

Assisted-by: Claude


>From 0721679dfd59de8102423d7e19c331f6af489ea8 Mon Sep 17 00:00:00 2001
From: Dhairyashil R G <dhairyashil25 at gmail.com>
Date: Fri, 25 Sep 2026 19:29:29 +0530
Subject: [PATCH] [mlir][vector] Add an eliminate-vector-masks pass

`eliminateVectorMasks` had no in-tree caller other than a test pass, so no
pipeline could use it. Expose it as an opt-in `-eliminate-vector-masks` pass and
drop the test pass.

The pass rewrites `vector.create_mask` operations that can be proven all-true
into `vector.constant_mask`, which canonicalization then folds away, turning a
masked transfer into an unmasked one. Mask sizes are bounded with
`ValueBoundsOpInterface`, so a size derived from a loop, such as `%dim - %iv`,
can be proven.

Proving a scalable dimension needs the range of `vscale`, given by the
`vscale-min` and `vscale-max` options. Leaving them unset means fixed-size
reasoning only, which suits a target without scalable vectors; a half-specified
or inverted range is rejected rather than silently ignored.

`eliminate-masks.mlir` keeps its checks and only switches its two RUN lines, so
the pass is shown to behave as the test pass did.

Context:
https://discourse.llvm.org/t/rfc-should-vector-transfer-read-write-keep-in-bounds-measurements-on-the-masking-alternative/91649

Assisted-by: Claude
---
 .../mlir/Dialect/Vector/Transforms/Passes.td  | 24 +++++++++++++
 .../Transforms/VectorMaskElimination.cpp      | 33 +++++++++++++++++
 .../eliminate-masks-invalid-options.mlir      | 11 ++++++
 mlir/test/Dialect/Vector/eliminate-masks.mlir |  4 +--
 .../Dialect/Vector/TestVectorTransforms.cpp   | 35 -------------------
 5 files changed, 70 insertions(+), 37 deletions(-)
 create mode 100644 mlir/test/Dialect/Vector/eliminate-masks-invalid-options.mlir

diff --git a/mlir/include/mlir/Dialect/Vector/Transforms/Passes.td b/mlir/include/mlir/Dialect/Vector/Transforms/Passes.td
index 9431a4d8e240f..d0abee7f87926 100644
--- a/mlir/include/mlir/Dialect/Vector/Transforms/Passes.td
+++ b/mlir/include/mlir/Dialect/Vector/Transforms/Passes.td
@@ -11,6 +11,30 @@
 
 include "mlir/Pass/PassBase.td"
 
+def EliminateVectorMasks : Pass<"eliminate-vector-masks", "func::FuncOp"> {
+  let summary = "Replace masks that are provably all-true with constant masks";
+  let description = [{
+    Rewrites `vector.create_mask` operations that can be proven all-true into
+    `vector.constant_mask`. Canonicalization then folds those away, turning a
+    masked transfer into an unmasked one.
+
+    A mask dimension is all-true when its size is at least the size of the
+    corresponding vector dimension. Sizes are bounded with
+    `ValueBoundsOpInterface`, so sizes derived from a loop, such as
+    `%dim - %iv`, can be proven too.
+
+    Proving a scalable dimension needs the range of `vscale`: pass `vscale-min`
+    and `vscale-max` when it is known for the target (1 and 16 for SVE, say).
+    Without a range, only fixed-size dimensions can be proven.
+  }];
+  let options = [
+    Option<"vscaleMin", "vscale-min", "unsigned", /*default=*/"0",
+           "Minimum possible value of vscale, or 0 when unknown.">,
+    Option<"vscaleMax", "vscale-max", "unsigned", /*default=*/"0",
+           "Maximum possible value of vscale, or 0 when unknown.">,
+  ];
+}
+
 def LowerVectorMaskPass : Pass<"lower-vector-mask", "func::FuncOp"> {
   let summary = "Lower 'vector.mask' operations";
   let constructor = "mlir::vector::createLowerVectorMaskPass()";
diff --git a/mlir/lib/Dialect/Vector/Transforms/VectorMaskElimination.cpp b/mlir/lib/Dialect/Vector/Transforms/VectorMaskElimination.cpp
index e6ee2b047de07..94111332ff5d3 100644
--- a/mlir/lib/Dialect/Vector/Transforms/VectorMaskElimination.cpp
+++ b/mlir/lib/Dialect/Vector/Transforms/VectorMaskElimination.cpp
@@ -8,9 +8,19 @@
 
 #include "mlir/Dialect/Utils/StaticValueUtils.h"
 #include "mlir/Dialect/Vector/IR/ScalableValueBoundsConstraintSet.h"
+#include "mlir/Dialect/Vector/Transforms/Passes.h"
 #include "mlir/Dialect/Vector/Transforms/VectorTransforms.h"
 #include "mlir/Interfaces/FunctionInterfaces.h"
 
+namespace mlir {
+namespace vector {
+
+#define GEN_PASS_DEF_ELIMINATEVECTORMASKS
+#include "mlir/Dialect/Vector/Transforms/Passes.h.inc"
+
+} // namespace vector
+} // namespace mlir
+
 using namespace mlir;
 using namespace mlir::vector;
 namespace {
@@ -136,4 +146,27 @@ void eliminateVectorMasks(IRRewriter &rewriter, FunctionOpInterface function,
     (void)resolveAllTrueCreateMaskOp(rewriter, mask, vscaleRange);
 }
 
+namespace {
+struct EliminateVectorMasksPass
+    : public impl::EliminateVectorMasksBase<EliminateVectorMasksPass> {
+  using Base::Base;
+
+  void runOnOperation() override {
+    std::optional<VscaleRange> vscaleRange;
+    if (vscaleMin || vscaleMax) {
+      if (!vscaleMin || !vscaleMax || vscaleMin > vscaleMax) {
+        getOperation()->emitError()
+            << "expected 'vscale-min' and 'vscale-max' to both be set, with "
+               "'vscale-min' <= 'vscale-max'";
+        return signalPassFailure();
+      }
+      vscaleRange = VscaleRange{vscaleMin, vscaleMax};
+    }
+
+    IRRewriter rewriter(&getContext());
+    eliminateVectorMasks(rewriter, getOperation(), vscaleRange);
+  }
+};
+} // namespace
+
 } // namespace mlir::vector
diff --git a/mlir/test/Dialect/Vector/eliminate-masks-invalid-options.mlir b/mlir/test/Dialect/Vector/eliminate-masks-invalid-options.mlir
new file mode 100644
index 0000000000000..a373cb6103db1
--- /dev/null
+++ b/mlir/test/Dialect/Vector/eliminate-masks-invalid-options.mlir
@@ -0,0 +1,11 @@
+// RUN: not mlir-opt %s -eliminate-vector-masks="vscale-min=2" 2>&1 | FileCheck %s
+// RUN: not mlir-opt %s -eliminate-vector-masks="vscale-min=8 vscale-max=4" 2>&1 | FileCheck %s
+
+/// A half-specified or inverted vscale range is rejected rather than silently
+/// ignored, which would leave scalable masks unproven for no visible reason.
+
+// CHECK: error: expected 'vscale-min' and 'vscale-max' to both be set, with 'vscale-min' <= 'vscale-max'
+func.func @vscale_range(%n: index) -> vector<[4]xi1> {
+  %mask = vector.create_mask %n : vector<[4]xi1>
+  return %mask : vector<[4]xi1>
+}
diff --git a/mlir/test/Dialect/Vector/eliminate-masks.mlir b/mlir/test/Dialect/Vector/eliminate-masks.mlir
index 002c502dc5fdb..22fe1aa8c83b1 100644
--- a/mlir/test/Dialect/Vector/eliminate-masks.mlir
+++ b/mlir/test/Dialect/Vector/eliminate-masks.mlir
@@ -1,5 +1,5 @@
-// RUN: mlir-opt %s -split-input-file -test-eliminate-vector-masks | FileCheck %s --check-prefixes=ALL,WITH-RANGE
-// RUN: mlir-opt %s -split-input-file -test-eliminate-vector-masks=fixed-size | FileCheck %s --check-prefixes=ALL,NO-RANGE
+// RUN: mlir-opt %s -split-input-file -eliminate-vector-masks="vscale-min=1 vscale-max=16" | FileCheck %s --check-prefixes=ALL,WITH-RANGE
+// RUN: mlir-opt %s -split-input-file -eliminate-vector-masks | FileCheck %s --check-prefixes=ALL,NO-RANGE
 
 // Each scalable test below is paired with a fixed-size equivalent, so that both
 // widths get the same coverage. Only the one case that needs a vscale range to
diff --git a/mlir/test/lib/Dialect/Vector/TestVectorTransforms.cpp b/mlir/test/lib/Dialect/Vector/TestVectorTransforms.cpp
index f26195793e0a2..ab4386167f7ab 100644
--- a/mlir/test/lib/Dialect/Vector/TestVectorTransforms.cpp
+++ b/mlir/test/lib/Dialect/Vector/TestVectorTransforms.cpp
@@ -1040,39 +1040,6 @@ struct TestVectorLinearize final
   }
 };
 
-struct TestEliminateVectorMasks
-    : public PassWrapper<TestEliminateVectorMasks,
-                         OperationPass<func::FuncOp>> {
-  MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestEliminateVectorMasks)
-
-  TestEliminateVectorMasks() = default;
-  TestEliminateVectorMasks(const TestEliminateVectorMasks &pass)
-      : PassWrapper(pass) {}
-
-  Option<unsigned> vscaleMin{
-      *this, "vscale-min", llvm::cl::desc("Minimum possible value of vscale."),
-      llvm::cl::init(1)};
-  Option<unsigned> vscaleMax{
-      *this, "vscale-max", llvm::cl::desc("Maximum possible value of vscale."),
-      llvm::cl::init(16)};
-  Option<bool> fixedSize{
-      *this, "fixed-size",
-      llvm::cl::desc("Run without a vscale range, as fixed-size code would."),
-      llvm::cl::init(false)};
-
-  StringRef getArgument() const final { return "test-eliminate-vector-masks"; }
-  StringRef getDescription() const final {
-    return "Test eliminating vector masks";
-  }
-  void runOnOperation() override {
-    IRRewriter rewriter(&getContext());
-    std::optional<VscaleRange> vscaleRange;
-    if (!fixedSize)
-      vscaleRange = VscaleRange{vscaleMin, vscaleMax};
-    eliminateVectorMasks(rewriter, getOperation(), vscaleRange);
-  }
-};
-
 struct TestVectorShuffleLowering
     : public PassWrapper<TestVectorShuffleLowering,
                          OperationPass<func::FuncOp>> {
@@ -1136,8 +1103,6 @@ void registerTestVectorLowerings() {
   PassRegistration<TestVectorLinearize>();
 
   PassRegistration<TestVectorBitWidthLinearize>();
-
-  PassRegistration<TestEliminateVectorMasks>();
 }
 } // namespace test
 } // namespace mlir



More information about the Mlir-commits mailing list