[Mlir-commits] [mlir] [mlir][vector] Decline in the parsers instead of asserting on bad input (PR #219917)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Mon Aug 31 02:03:54 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-mlir-vector

Author: Alessandro Potenza (alepot55)

<details>
<summary>Changes</summary>

Three custom parsers in the Vector dialect reach a cast or an index that the input does not have to satisfy, so `mlir-opt` aborts while parsing, before any verifier can report the problem.

`vector.outerproduct` builds its result type from dimension 0 of the operands. A 0-d vector has no dimension 0, so `getScalableDims()[0]` runs off the end:

```mlir
vector.outerproduct %a, %b : vector<f32>, vector<f32>
```
```
Assertion `Index < Length && "Invalid index!"' failed.
```

`vector.transfer_read` and `vector.transfer_write` take `permutation_map` out of the attribute dictionary and cast it to `AffineMapAttr` without checking:

```mlir
vector.transfer_read %m[%i], %f {permutation_map = 42 : i64} : memref<4xf32>, vector<4xf32>
```
```
Assertion `isa<To>(Val) && "cast<Ty>() argument of incompatible type!"' failed.
```

`vector.contract` casts the two operand types to `VectorType` on the path that resolves the mask operands:

```mlir
vector.contract {...} %a, %b, %c, %m0, %m1 : f32, f32 into f32
```
```
Assertion `isa<To>(Val) && "cast<Ty>() argument of incompatible type!"' failed.
```

Each one now emits a diagnostic instead.

The `outerproduct` check is deliberately limited to rank 0, which is the only rank the parser itself cannot handle. A higher rank still reaches the verifier, which rejects it with the same wording, and the existing test for that keeps testing the verifier. An earlier version of this patch rejected every rank other than 1 and broke that test, which is how the narrower condition was arrived at.

This is the same family as #<!-- -->133434, which fixed the unchecked `iterator_types` cast in this same `vector.contract` parser in 2025.

## Testing

Verified by execution on `11e915f2b75d`: the three inputs above abort without the patch and produce the diagnostics with it. `mlir/test/Dialect/Vector`, `Conversion/VectorToSCF`, `Conversion/VectorToGPU` and `Conversion/VectorToLLVM` are green, 119 tests. Across `mlir/test/Dialect` and `mlir/test/Conversion`, 1697 of 1707 pass, with 8 failures in `Dialect/LLVMIR` and `Dialect/X86` that are identical on the unpatched baseline and one expected failure.

`clang-format` is clean against the merge base.

Assisted-by: Claude (Anthropic)

AI-assisted, disclosed per the LLVM AI Tool Use Policy.


---
Full diff: https://github.com/llvm/llvm-project/pull/219917.diff


2 Files Affected:

- (modified) mlir/lib/Dialect/Vector/IR/VectorOps.cpp (+28-4) 
- (modified) mlir/test/Dialect/Vector/invalid.mlir (+42) 


``````````diff
diff --git a/mlir/lib/Dialect/Vector/IR/VectorOps.cpp b/mlir/lib/Dialect/Vector/IR/VectorOps.cpp
index f8f3deb2e4789..f88c40acc553c 100644
--- a/mlir/lib/Dialect/Vector/IR/VectorOps.cpp
+++ b/mlir/lib/Dialect/Vector/IR/VectorOps.cpp
@@ -963,8 +963,13 @@ ParseResult ContractionOp::parse(OpAsmParser &parser, OperationState &result) {
   if (masksInfo.size() != 2)
     return parser.emitError(parser.getNameLoc(),
                             "expected zero or exactly 2 vector mask operands");
-  auto lhsType = llvm::cast<VectorType>(types[0]);
-  auto rhsType = llvm::cast<VectorType>(types[1]);
+  auto lhsType = llvm::dyn_cast<VectorType>(types[0]);
+  auto rhsType = llvm::dyn_cast<VectorType>(types[1]);
+  if (!lhsType || !rhsType)
+    return parser.emitError(
+        parser.getNameLoc(),
+        "expected vector types for the lhs and rhs operands "
+        "when mask operands are present");
   auto maskElementType = parser.getBuilder().getI1Type();
   std::array<VectorType, 2> maskTypes = {
       VectorType::Builder(lhsType).setElementType(maskElementType),
@@ -4379,6 +4384,15 @@ ParseResult OuterProductOp::parse(OpAsmParser &parser, OperationState &result) {
   if (!vLHS)
     return parser.emitError(parser.getNameLoc(),
                             "expected vector type for operand #1");
+  // The result type is built below from dimension 0 of the operands, which a
+  // 0-d vector does not have. Only that case has to be caught here; a higher
+  // rank still reaches the verifier, which rejects it with the same wording.
+  if (vLHS.getRank() == 0)
+    return parser.emitError(parser.getNameLoc(),
+                            "expected 1-d vector for operand #1");
+  if (vRHS && vRHS.getRank() == 0)
+    return parser.emitError(parser.getNameLoc(),
+                            "expected 1-d vector for operand #2");
 
   VectorType resType;
   if (vRHS) {
@@ -5285,7 +5299,12 @@ ParseResult TransferReadOp::parse(OpAsmParser &parser, OperationState &result) {
     permMap = getTransferMinorIdentityMap(shapedType, vectorType);
     result.attributes.set(permMapAttrName, AffineMapAttr::get(permMap));
   } else {
-    permMap = llvm::cast<AffineMapAttr>(permMapAttr).getValue();
+    auto affineMapAttr = llvm::dyn_cast<AffineMapAttr>(permMapAttr);
+    if (!affineMapAttr)
+      return parser.emitError(parser.getNameLoc(),
+                              "expected an affine map attribute for the "
+                              "permutation_map attribute");
+    permMap = affineMapAttr.getValue();
   }
   auto inBoundsAttrName = TransferReadOp::getInBoundsAttrName(result.name);
   Attribute inBoundsAttr = result.attributes.get(inBoundsAttrName);
@@ -5775,7 +5794,12 @@ ParseResult TransferWriteOp::parse(OpAsmParser &parser,
     permMap = getTransferMinorIdentityMap(shapedType, vectorType);
     result.attributes.set(permMapAttrName, AffineMapAttr::get(permMap));
   } else {
-    permMap = llvm::cast<AffineMapAttr>(permMapAttr).getValue();
+    auto affineMapAttr = llvm::dyn_cast<AffineMapAttr>(permMapAttr);
+    if (!affineMapAttr)
+      return parser.emitError(parser.getNameLoc(),
+                              "expected an affine map attribute for the "
+                              "permutation_map attribute");
+    permMap = affineMapAttr.getValue();
   }
   auto inBoundsAttrName = TransferWriteOp::getInBoundsAttrName(result.name);
   Attribute inBoundsAttr = result.attributes.get(inBoundsAttrName);
diff --git a/mlir/test/Dialect/Vector/invalid.mlir b/mlir/test/Dialect/Vector/invalid.mlir
index 489f5489030fd..3ac92633ce752 100644
--- a/mlir/test/Dialect/Vector/invalid.mlir
+++ b/mlir/test/Dialect/Vector/invalid.mlir
@@ -251,6 +251,20 @@ func.func @outerproduct_non_vector_operand(%arg0: f32) {
 
 // -----
 
+func.func @outerproduct_zero_rank_lhs(%arg0: vector<f32>, %arg1: vector<4xf32>) {
+  // expected-error at +1 {{expected 1-d vector for operand #1}}
+  %1 = vector.outerproduct %arg0, %arg1 : vector<f32>, vector<4xf32>
+}
+
+// -----
+
+func.func @outerproduct_zero_rank_rhs(%arg0: vector<4xf32>, %arg1: vector<f32>) {
+  // expected-error at +1 {{expected 1-d vector for operand #2}}
+  %1 = vector.outerproduct %arg0, %arg1 : vector<4xf32>, vector<f32>
+}
+
+// -----
+
 func.func @outerproduct_invalid_kind_attr(%arg0 : vector<[4]xf32>, %arg1 : vector<[8]xf32>) {
   // expected-error at +1 {{expected 'kind' attribute of type CombiningKind (e.g. 'vector.kind<add>')}}
   %0 = vector.outerproduct %arg0, %arg1 {kind = "invalid"} : vector<[4]xf32>, vector<[8]xf32>
@@ -2416,3 +2430,31 @@ func.func @load_negative_stride(%src: memref<100x100xf32, strided<[-100, 1]>>) -
   %v = vector.load %src[%c0, %c0] : memref<100x100xf32, strided<[-100, 1]>>, vector<8xf32>
   return %v : vector<8xf32>
 }
+
+// -----
+
+func.func @transfer_read_permutation_map_not_an_affine_map(%arg0: memref<4xf32>,
+    %i: index, %f: f32) {
+  // expected-error at +1 {{expected an affine map attribute for the permutation_map attribute}}
+  %0 = vector.transfer_read %arg0[%i], %f {permutation_map = 42 : i64} : memref<4xf32>, vector<4xf32>
+}
+
+// -----
+
+func.func @transfer_write_permutation_map_not_an_affine_map(%v: vector<4xf32>,
+    %arg0: memref<4xf32>, %i: index) {
+  // expected-error at +1 {{expected an affine map attribute for the permutation_map attribute}}
+  vector.transfer_write %v, %arg0[%i] {permutation_map = 42 : i64} : vector<4xf32>, memref<4xf32>
+}
+
+// -----
+
+#map0 = affine_map<(d0) -> (d0)>
+#map1 = affine_map<(d0) -> ()>
+func.func @contraction_masked_non_vector_operands(%a: f32, %b: f32, %c: f32,
+    %m0: vector<4xi1>, %m1: vector<4xi1>) {
+  // expected-error at +1 {{expected vector types for the lhs and rhs operands when mask operands are present}}
+  %0 = vector.contract {indexing_maps = [#map0, #map0, #map1],
+                        iterator_types = ["reduction"], kind = #vector.kind<add>}
+       %a, %b, %c, %m0, %m1 : f32, f32 into f32
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/219917


More information about the Mlir-commits mailing list