From e289b2e765ed279e57e4643a4f602d2328ac4af4 Mon Sep 17 00:00:00 2001 From: Nick Kreeger Date: Mon, 12 Jan 2026 10:52:44 -0600 Subject: [PATCH] [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. --- .../mlir/Dialect/Utils/VerificationUtils.h | 32 +++++++++++++++++++ .../Bufferization/IR/BufferizationOps.cpp | 7 ++-- mlir/lib/Dialect/GPU/IR/GPUDialect.cpp | 7 ++-- mlir/lib/Dialect/MemRef/IR/MemRefOps.cpp | 11 +++---- mlir/lib/Dialect/Tensor/IR/TensorOps.cpp | 21 +++++------- mlir/lib/Dialect/Utils/CMakeLists.txt | 1 + mlir/lib/Dialect/Utils/VerificationUtils.cpp | 22 +++++++++++++ mlir/test/Dialect/Bufferization/invalid.mlir | 2 +- mlir/test/Dialect/GPU/invalid.mlir | 4 +-- mlir/test/Dialect/MemRef/invalid.mlir | 4 +-- mlir/test/Dialect/Tensor/invalid.mlir | 2 +- 11 files changed, 82 insertions(+), 31 deletions(-) create mode 100644 mlir/include/mlir/Dialect/Utils/VerificationUtils.h create mode 100644 mlir/lib/Dialect/Utils/VerificationUtils.cpp diff --git a/mlir/include/mlir/Dialect/Utils/VerificationUtils.h b/mlir/include/mlir/Dialect/Utils/VerificationUtils.h new file mode 100644 index 000000000000..c1c3cc6231eb --- /dev/null +++ b/mlir/include/mlir/Dialect/Utils/VerificationUtils.h @@ -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 diff --git a/mlir/lib/Dialect/Bufferization/IR/BufferizationOps.cpp b/mlir/lib/Dialect/Bufferization/IR/BufferizationOps.cpp index 56ff2121e462..eda6bf276be0 100644 --- a/mlir/lib/Dialect/Bufferization/IR/BufferizationOps.cpp +++ b/mlir/lib/Dialect/Bufferization/IR/BufferizationOps.cpp @@ -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 @@ -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(); diff --git a/mlir/lib/Dialect/GPU/IR/GPUDialect.cpp b/mlir/lib/Dialect/GPU/IR/GPUDialect.cpp index 552485e74812..ff2d792f9ceb 100644 --- a/mlir/lib/Dialect/GPU/IR/GPUDialect.cpp +++ b/mlir/lib/Dialect/GPU/IR/GPUDialect.cpp @@ -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(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()) diff --git a/mlir/lib/Dialect/MemRef/IR/MemRefOps.cpp b/mlir/lib/Dialect/MemRef/IR/MemRefOps.cpp index fe93b3e29640..a612475edf3b 100644 --- a/mlir/lib/Dialect/MemRef/IR/MemRefOps.cpp +++ b/mlir/lib/Dialect/MemRef/IR/MemRefOps.cpp @@ -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(); } diff --git a/mlir/lib/Dialect/Tensor/IR/TensorOps.cpp b/mlir/lib/Dialect/Tensor/IR/TensorOps.cpp index a0c7e40c20a4..11824a0ac6f0 100644 --- a/mlir/lib/Dialect/Tensor/IR/TensorOps.cpp +++ b/mlir/lib/Dialect/Tensor/IR/TensorOps.cpp @@ -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(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 diff --git a/mlir/lib/Dialect/Utils/CMakeLists.txt b/mlir/lib/Dialect/Utils/CMakeLists.txt index f5bb687ae071..7673da593230 100644 --- a/mlir/lib/Dialect/Utils/CMakeLists.txt +++ b/mlir/lib/Dialect/Utils/CMakeLists.txt @@ -3,6 +3,7 @@ add_mlir_library(MLIRDialectUtils ReshapeOpsUtils.cpp StructuredOpsUtils.cpp StaticValueUtils.cpp + VerificationUtils.cpp DEPENDS MLIRDialectUtilsIncGen diff --git a/mlir/lib/Dialect/Utils/VerificationUtils.cpp b/mlir/lib/Dialect/Utils/VerificationUtils.cpp new file mode 100644 index 000000000000..22b224713a6a --- /dev/null +++ b/mlir/lib/Dialect/Utils/VerificationUtils.cpp @@ -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(); +} diff --git a/mlir/test/Dialect/Bufferization/invalid.mlir b/mlir/test/Dialect/Bufferization/invalid.mlir index 9884b040119d..76aba14bc50f 100644 --- a/mlir/test/Dialect/Bufferization/invalid.mlir +++ b/mlir/test/Dialect/Bufferization/invalid.mlir @@ -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 } diff --git a/mlir/test/Dialect/GPU/invalid.mlir b/mlir/test/Dialect/GPU/invalid.mlir index 26bcf948bc85..5e9c25c36aa7 100644 --- a/mlir/test/Dialect/GPU/invalid.mlir +++ b/mlir/test/Dialect/GPU/invalid.mlir @@ -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 } diff --git a/mlir/test/Dialect/MemRef/invalid.mlir b/mlir/test/Dialect/MemRef/invalid.mlir index d10651f36371..2dfe733c1376 100644 --- a/mlir/test/Dialect/MemRef/invalid.mlir +++ b/mlir/test/Dialect/MemRef/invalid.mlir @@ -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 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 } diff --git a/mlir/test/Dialect/Tensor/invalid.mlir b/mlir/test/Dialect/Tensor/invalid.mlir index f36678c3d758..63be5493e893 100644 --- a/mlir/test/Dialect/Tensor/invalid.mlir +++ b/mlir/test/Dialect/Tensor/invalid.mlir @@ -100,7 +100,7 @@ func.func @tensor.from_elements_wrong_elements_count() { func.func @tensor.generate(%m : index) -> tensor { - // 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