[mlir][Utils] Add VerificationUtils (NFC) (#174336)

Introduces `VerificationUtils` to consolidate common operation
verification patterns in MLIR. This initial implementation provides
`verifyDynamicDimensionCount()` to reduce code duplication across
dialect verifiers.

This is an NFC (No Functional Change) refactoring that improves code
maintainability by extracting reusable verification logic into a shared
utility.
This commit is contained in:
Nick Kreeger 2026-01-12 10:52:44 -06:00 committed by GitHub
parent ab34189878
commit e289b2e765
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 82 additions and 31 deletions

View file

@ -0,0 +1,32 @@
//===- VerificationUtils.h - Common verification utilities ------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This header file defines common verification utilities that can be shared
// across multiple MLIR dialects. These utilities help reduce code duplication
// for common verification patterns.
//
//===----------------------------------------------------------------------===//
#ifndef MLIR_DIALECT_UTILS_VERIFICATIONUTILS_H
#define MLIR_DIALECT_UTILS_VERIFICATIONUTILS_H
#include "mlir/IR/BuiltinTypes.h"
#include "mlir/IR/Operation.h"
#include "mlir/Support/LLVM.h"
namespace mlir {
/// Verify that the number of dynamic size operands matches the number of
/// dynamic dimensions in the shaped type. Returns failure and emits an error
/// if the counts don't match.
LogicalResult verifyDynamicDimensionCount(Operation *op, ShapedType type,
ValueRange dynamicSizes);
} // namespace mlir
#endif // MLIR_DIALECT_UTILS_VERIFICATIONUTILS_H

View file

@ -12,6 +12,7 @@
#include "mlir/Dialect/Func/IR/FuncOps.h"
#include "mlir/Dialect/MemRef/IR/MemRef.h"
#include "mlir/Dialect/Tensor/IR/Tensor.h"
#include "mlir/Dialect/Utils/VerificationUtils.h"
#include "mlir/IR/Matchers.h"
#include <optional>
@ -251,9 +252,9 @@ AllocTensorOp::getBufferType(Value value, const BufferizationOptions &options,
LogicalResult AllocTensorOp::verify() {
if (getCopy() && !getDynamicSizes().empty())
return emitError("dynamic sizes not needed when copying a tensor");
if (!getCopy() && getType().getNumDynamicDims() != getDynamicSizes().size())
return emitError("expected ")
<< getType().getNumDynamicDims() << " dynamic sizes";
if (!getCopy() && failed(verifyDynamicDimensionCount(
getOperation(), getType(), getDynamicSizes())))
return failure();
if (getCopy() && getCopy().getType() != getType())
return emitError("expected that `copy` and return type match");
return success();

View file

@ -16,6 +16,7 @@
#include "mlir/Dialect/Bufferization/IR/BufferDeallocationOpInterface.h"
#include "mlir/Dialect/Math/IR/Math.h"
#include "mlir/Dialect/MemRef/IR/MemRef.h"
#include "mlir/Dialect/Utils/VerificationUtils.h"
#include "mlir/IR/Attributes.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/BuiltinAttributes.h"
@ -2171,9 +2172,9 @@ void WaitOp::getCanonicalizationPatterns(RewritePatternSet &results,
LogicalResult AllocOp::verify() {
auto memRefType = llvm::cast<MemRefType>(getMemref().getType());
if (getDynamicSizes().size() != memRefType.getNumDynamicDims())
return emitOpError("dimension operand count does not equal memref "
"dynamic dimension count");
if (failed(verifyDynamicDimensionCount(getOperation(), memRefType,
getDynamicSizes())))
return failure();
unsigned numSymbols = 0;
if (!memRefType.getLayout().isIdentity())

View file

@ -10,6 +10,7 @@
#include "mlir/Dialect/Arith/Utils/Utils.h"
#include "mlir/Dialect/MemRef/IR/MemRef.h"
#include "mlir/Dialect/Utils/StaticValueUtils.h"
#include "mlir/Dialect/Utils/VerificationUtils.h"
#include "mlir/IR/AffineMap.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/BuiltinTypes.h"
@ -192,9 +193,8 @@ static LogicalResult verifyAllocLikeOp(AllocLikeOp op) {
if (!memRefType)
return op.emitOpError("result must be a memref");
if (op.getDynamicSizes().size() != memRefType.getNumDynamicDims())
return op.emitOpError("dimension operand count does not equal memref "
"dynamic dimension count");
if (failed(verifyDynamicDimensionCount(op, memRefType, op.getDynamicSizes())))
return failure();
unsigned numSymbols = 0;
if (!memRefType.getLayout().isIdentity())
@ -3735,9 +3735,8 @@ LogicalResult ViewOp::verify() {
<< baseType << " and view memref type " << viewType;
// Verify that we have the correct number of sizes for the result type.
unsigned numDynamicDims = viewType.getNumDynamicDims();
if (getSizes().size() != numDynamicDims)
return emitError("incorrect number of size operands for type ") << viewType;
if (failed(verifyDynamicDimensionCount(getOperation(), viewType, getSizes())))
return failure();
return success();
}

View file

@ -15,6 +15,7 @@
#include "mlir/Dialect/Utils/IndexingUtils.h"
#include "mlir/Dialect/Utils/ReshapeOpsUtils.h"
#include "mlir/Dialect/Utils/StaticValueUtils.h"
#include "mlir/Dialect/Utils/VerificationUtils.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/BuiltinAttributeInterfaces.h"
#include "mlir/IR/BuiltinTypeInterfaces.h"
@ -1075,11 +1076,8 @@ void EmptyOp::build(OpBuilder &builder, OperationState &result,
}
LogicalResult EmptyOp::verify() {
if (getType().getNumDynamicDims() != getDynamicSizes().size())
return emitOpError("incorrect number of dynamic sizes, has ")
<< getDynamicSizes().size() << ", expected "
<< getType().getNumDynamicDims();
return success();
return verifyDynamicDimensionCount(getOperation(), getType(),
getDynamicSizes());
}
LogicalResult
@ -1666,9 +1664,9 @@ LogicalResult GenerateOp::verify() {
// Ensure that the tensor type has as many dynamic dimensions as are
// specified by the operands.
RankedTensorType resultType = llvm::cast<RankedTensorType>(getType());
if (getNumOperands() != resultType.getNumDynamicDims())
return emitError("must have as many index operands as dynamic extents "
"in the result type");
if (failed(verifyDynamicDimensionCount(getOperation(), resultType,
getOperands())))
return failure();
return success();
}
@ -4039,11 +4037,8 @@ void SplatOp::getAsmResultNames(
}
LogicalResult SplatOp::verify() {
if (getType().getNumDynamicDims() != getDynamicSizes().size())
return emitOpError("incorrect number of dynamic sizes, has ")
<< getDynamicSizes().size() << ", expected "
<< getType().getNumDynamicDims();
return success();
return verifyDynamicDimensionCount(getOperation(), getType(),
getDynamicSizes());
}
LogicalResult

View file

@ -3,6 +3,7 @@ add_mlir_library(MLIRDialectUtils
ReshapeOpsUtils.cpp
StructuredOpsUtils.cpp
StaticValueUtils.cpp
VerificationUtils.cpp
DEPENDS
MLIRDialectUtilsIncGen

View file

@ -0,0 +1,22 @@
//===- VerificationUtils.cpp - Common verification utilities --------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "mlir/Dialect/Utils/VerificationUtils.h"
using namespace mlir;
LogicalResult mlir::verifyDynamicDimensionCount(Operation *op, ShapedType type,
ValueRange dynamicSizes) {
int64_t expectedCount = type.getNumDynamicDims();
int64_t actualCount = dynamicSizes.size();
if (expectedCount != actualCount) {
return op->emitOpError("incorrect number of dynamic sizes, has ")
<< actualCount << ", expected " << expectedCount;
}
return success();
}

View file

@ -2,7 +2,7 @@
func.func @alloc_tensor_missing_dims(%arg0: index)
{
// expected-error @+1 {{expected 2 dynamic sizes}}
// expected-error @+1 {{incorrect number of dynamic sizes, has 1, expected 2}}
%0 = bufferization.alloc_tensor(%arg0) : tensor<4x?x?x5xf32>
return
}

View file

@ -777,7 +777,7 @@ func.func @alloc() {
// Number of dynamic dimension operand count greater than memref dynamic dimension count.
func.func @alloc() {
%0 = arith.constant 7 : index
// expected-error@+1 {{dimension operand count does not equal memref dynamic dimension count}}
// expected-error@+1 {{incorrect number of dynamic sizes, has 2, expected 1}}
%1 = gpu.alloc(%0, %0) : memref<2x?xf32, 1>
return
}
@ -787,7 +787,7 @@ func.func @alloc() {
// Number of dynamic dimension operand count less than memref dynamic dimension count.
func.func @alloc() {
%0 = arith.constant 7 : index
// expected-error@+1 {{dimension operand count does not equal memref dynamic dimension count}}
// expected-error@+1 {{incorrect number of dynamic sizes, has 1, expected 2}}
%1 = gpu.alloc(%0) : memref<2x?x?xf32, 1>
return
}

View file

@ -624,7 +624,7 @@ func.func @invalid_view(%arg0 : index, %arg1 : index, %arg2 : index) {
func.func @invalid_view(%arg0 : index, %arg1 : index, %arg2 : index) {
%0 = memref.alloc() : memref<2048xi8>
// expected-error@+1 {{incorrect number of size operands for type}}
// expected-error@+1 {{incorrect number of dynamic sizes, has 1, expected 2}}
%1 = memref.view %0[%arg2][%arg0]
: memref<2048xi8> to memref<?x?xf32>
return
@ -927,7 +927,7 @@ func.func @bad_alloc_wrong_dynamic_dim_count() {
^bb0:
%0 = arith.constant 7 : index
// Test alloc with wrong number of dynamic dimensions.
// expected-error@+1 {{dimension operand count does not equal memref dynamic dimension count}}
// expected-error@+1 {{incorrect number of dynamic sizes, has 1, expected 0}}
%1 = memref.alloc(%0)[%0] : memref<2x4xf32, affine_map<(d0, d1)[s0] -> ((d0 + s0), d1)>, 1>
return
}

View file

@ -100,7 +100,7 @@ func.func @tensor.from_elements_wrong_elements_count() {
func.func @tensor.generate(%m : index)
-> tensor<?x3x?xf32> {
// expected-error @+1 {{must have as many index operands as dynamic extents in the result type}}
// expected-error @+1 {{incorrect number of dynamic sizes, has 1, expected 2}}
%tnsr = tensor.generate %m {
^bb0(%i : index, %j : index, %k : index):
%elem = arith.constant 8.0 : f32