[Mlir-commits] [mlir] f6ddf39 - [mlir][ArmNeon] Reject masked contractions in `I8MM/BFMMLA` lowering patterns (#213698)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Thu Aug 6 03:26:47 PDT 2026
Author: Federico Bruzzone
Date: 2026-08-06T12:26:41+02:00
New Revision: f6ddf3965910afb16285418fe7bbb9ca36a8b18e
URL: https://github.com/llvm/llvm-project/commit/f6ddf3965910afb16285418fe7bbb9ca36a8b18e
DIFF: https://github.com/llvm/llvm-project/commit/f6ddf3965910afb16285418fe7bbb9ca36a8b18e.diff
LOG: [mlir][ArmNeon] Reject masked contractions in `I8MM/BFMMLA` lowering patterns (#213698)
`LowerContractionToNeonI8MMPattern` and
`LowerContractionToNeonBFMMLAPattern` rewrite a `vector.contract` into
several ops (multiple `arm_neon.intr.smmla`/`bfmmla` calls plus
`extract/insert/shape_cast` scaffolding).
When the matched `vector.contract` sits inside a `vector.mask` (e.g.,
from vectorizing a matmul with dynamically-shaped operands), this
in-place multi-op rewrite violates `vector.mask`'s invariant that its
region contain exactly **one** operation, and the verifier rejects the
result: _error: 'vector.mask' op expects only one operation to mask_.
Neither pattern checked for masking, unlike the generic
contraction-lowering
patterns. To fix the problem, we bail out via `notifyMatchFailure` when
the contraction is masked, so it falls through to a different lowering
path instead of producing invalid IR.
Found while adding neon `linalg.matmul` e2e tests in #212809.
AI was used to investigate the problem.
---------
Signed-off-by: Federico Bruzzone <federico.bruzzone.i at gmail.com>
Added:
Modified:
mlir/lib/Dialect/ArmNeon/Transforms/LowerContractToNeonPatterns.cpp
mlir/test/Dialect/ArmNeon/lower-to-arm-neon.mlir
Removed:
################################################################################
diff --git a/mlir/lib/Dialect/ArmNeon/Transforms/LowerContractToNeonPatterns.cpp b/mlir/lib/Dialect/ArmNeon/Transforms/LowerContractToNeonPatterns.cpp
index fd0a774dcf602..6b4285026ea04 100644
--- a/mlir/lib/Dialect/ArmNeon/Transforms/LowerContractToNeonPatterns.cpp
+++ b/mlir/lib/Dialect/ArmNeon/Transforms/LowerContractToNeonPatterns.cpp
@@ -457,6 +457,9 @@ class LowerContractionToNeonI8MMPattern
using OpRewritePattern::OpRewritePattern;
LogicalResult matchAndRewrite(vector::ContractionOp op,
PatternRewriter &rewriter) const override {
+ if (cast<vector::MaskableOpInterface>(op.getOperation()).isMasked())
+ return rewriter.notifyMatchFailure(
+ op, "masked contractions are not supported");
VectorContractRewriterI8MM vcr;
if (failed(vcr.matchAndInit(op, rewriter)))
@@ -473,6 +476,9 @@ class LowerContractionToNeonBFMMLAPattern
using OpRewritePattern::OpRewritePattern;
LogicalResult matchAndRewrite(vector::ContractionOp op,
PatternRewriter &rewriter) const override {
+ if (cast<vector::MaskableOpInterface>(op.getOperation()).isMasked())
+ return rewriter.notifyMatchFailure(
+ op, "masked contractions are not supported");
VectorContractRewriterBFMMLA vcr;
if (failed(vcr.matchAndInit(op, rewriter)))
diff --git a/mlir/test/Dialect/ArmNeon/lower-to-arm-neon.mlir b/mlir/test/Dialect/ArmNeon/lower-to-arm-neon.mlir
index 5fc29c6442602..a8799028217df 100644
--- a/mlir/test/Dialect/ArmNeon/lower-to-arm-neon.mlir
+++ b/mlir/test/Dialect/ArmNeon/lower-to-arm-neon.mlir
@@ -515,6 +515,27 @@ func.func @vector_arm_neon_mnk_unroll(%lhs: vector<4x16xi8>, %rhs: vector<4x16xi
return %res : vector<4x4xi32>
}
+// -----
+
+// Masked contractions must be left alone: rewriting one in place would leave
+// more than one op inside the `vector.mask` region, which is invalid.
+
+// CHECK-LABEL: func @vector_arm_neon_masked_contract_not_lowered
+// CHECK: %[[RES:.*]] = vector.mask %{{.*}} { vector.contract
+// CHECK-NOT: arm_neon.intr.smmla
+// CHECK: return %[[RES]]
+func.func @vector_arm_neon_masked_contract_not_lowered(
+ %lhs: vector<4x8xi8>, %rhs: vector<4x8xi8>, %acc: vector<4x4xi32>,
+ %m: index, %n: index, %k: index) -> vector<4x4xi32> {
+ %mask = vector.create_mask %m, %n, %k : vector<4x4x8xi1>
+ %lhs_extsi = arith.extsi %lhs : vector<4x8xi8> to vector<4x8xi32>
+ %rhs_extsi = arith.extsi %rhs : vector<4x8xi8> to vector<4x8xi32>
+ %res = vector.mask %mask {
+ vector.contract {indexing_maps = [affine_map<(d0, d1, d2) -> (d0, d2)>, affine_map<(d0, d1, d2) -> (d1, d2)>, affine_map<(d0, d1, d2) -> (d0, d1)>], iterator_types = ["parallel", "parallel", "reduction"], kind = #vector.kind<add>} %lhs_extsi, %rhs_extsi, %acc : vector<4x8xi32>, vector<4x8xi32> into vector<4x4xi32>
+ } : vector<4x4x8xi1> -> vector<4x4xi32>
+ return %res : vector<4x4xi32>
+}
+
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%module: !transform.any_op {transform.readonly}) {
%func = transform.structured.match ops{["func.func"]} in %module : (!transform.any_op) -> !transform.op<"func.func">
More information about the Mlir-commits
mailing list