[mlir] Fix verification order of nested ops.

In order to increase parallism, certain ops with regions and have the
IsIsolatedFromAbove trait will have their verification delayed. That
means the region verifier may access the invalid ops and may lead to a
crash.

Reviewed By: rriddle

Differential Revision: https://reviews.llvm.org/D122771
This commit is contained in:
Chia-hung Duan 2022-04-15 04:33:40 +00:00
parent 90a17ef6cc
commit 5232c5c5d4
7 changed files with 120 additions and 25 deletions

View File

@ -52,15 +52,14 @@ public:
LogicalResult verifyOpAndDominance(Operation &op);
private:
/// Any ops that have regions and are marked as "isolated from above" will be
/// returned in the opsWithIsolatedRegions vector.
LogicalResult
verifyBlock(Block &block,
SmallVectorImpl<Operation *> &opsWithIsolatedRegions);
/// Verify the properties and dominance relationships of this operation,
/// stopping region recursion at any "isolated from above operations". Any
/// such ops are returned in the opsWithIsolatedRegions vector.
LogicalResult
verifyOperation(Operation &op,
SmallVectorImpl<Operation *> &opsWithIsolatedRegions);
/// Verify the properties and dominance relationships of this operation.
LogicalResult verifyOperation(Operation &op);
/// Verify the dominance property of regions contained within the given
/// Operation.
@ -74,10 +73,8 @@ private:
} // namespace
LogicalResult OperationVerifier::verifyOpAndDominance(Operation &op) {
SmallVector<Operation *> opsWithIsolatedRegions;
// Verify the operation first, collecting any IsolatedFromAbove operations.
if (failed(verifyOperation(op, opsWithIsolatedRegions)))
if (failed(verifyOperation(op)))
return failure();
// Since everything looks structurally ok to this point, we do a dominance
@ -90,15 +87,7 @@ LogicalResult OperationVerifier::verifyOpAndDominance(Operation &op) {
return failure();
}
// If we aren't verifying nested operations, then we're done.
if (!verifyRecursively)
return success();
// Otherwise, check the dominance properties and invariants of any operations
// in the regions contained by the 'opsWithIsolatedRegions' operations.
return failableParallelForEach(
op.getContext(), opsWithIsolatedRegions,
[&](Operation *op) { return verifyOpAndDominance(*op); });
return success();
}
/// Returns true if this block may be valid without terminator. That is if:
@ -150,7 +139,7 @@ LogicalResult OperationVerifier::verifyBlock(
opsWithIsolatedRegions.push_back(&op);
// Otherwise, check the operation inline.
} else if (failed(verifyOperation(op, opsWithIsolatedRegions))) {
} else if (failed(verifyOperation(op))) {
return failure();
}
}
@ -177,8 +166,7 @@ LogicalResult OperationVerifier::verifyBlock(
/// Verify the properties and dominance relationships of this operation,
/// stopping region recursion at any "isolated from above operations". Any such
/// ops are returned in the opsWithIsolatedRegions vector.
LogicalResult OperationVerifier::verifyOperation(
Operation &op, SmallVectorImpl<Operation *> &opsWithIsolatedRegions) {
LogicalResult OperationVerifier::verifyOperation(Operation &op) {
// Check that operands are non-nil and structurally ok.
for (auto operand : op.getOperands())
if (!operand)
@ -198,6 +186,8 @@ LogicalResult OperationVerifier::verifyOperation(
if (registeredInfo && failed(registeredInfo->verifyInvariants(&op)))
return failure();
SmallVector<Operation *> opsWithIsolatedRegions;
if (unsigned numRegions = op.getNumRegions()) {
auto kindInterface = dyn_cast<RegionKindInterface>(op);
@ -238,6 +228,12 @@ LogicalResult OperationVerifier::verifyOperation(
}
}
// Verify the nested ops that are able to be verified in parallel.
if (failed(failableParallelForEach(
op.getContext(), opsWithIsolatedRegions,
[&](Operation *op) { return verifyOpAndDominance(*op); })))
return failure();
// After the region ops are verified, run the verifiers that have additional
// region invariants need to veirfy.
if (registeredInfo && failed(registeredInfo->verifyRegionInvariants(&op)))

View File

@ -8,7 +8,7 @@ func @unsupported_attribute() {
// -----
func @return_i32_f32() -> (i32, f32)
func private @return_i32_f32() -> (i32, f32)
func @call() {
// expected-error @+3 {{op result type mismatch at index 0}}

View File

@ -351,7 +351,7 @@ func @nonexistent_global_memref() {
// -----
func @foo()
func private @foo()
func @nonexistent_global_memref() {
// expected-error @+1 {{'foo' does not reference a valid global memref}}

View File

@ -565,8 +565,8 @@ func @func_variadic(...)
// -----
func @redef() // expected-note {{see existing symbol definition here}}
func @redef() // expected-error {{redefinition of symbol named 'redef'}}
func private @redef() // expected-note {{see existing symbol definition here}}
func private @redef() // expected-error {{redefinition of symbol named 'redef'}}
// -----

View File

@ -0,0 +1,55 @@
// RUN: mlir-opt --mlir-disable-threading -split-input-file -verify-diagnostics %s
func @verify_operand_type() {
%0 = arith.constant 1 : index
// expected-error@+1 {{op operand #0 must be 32-bit signless integer, but got 'index'}}
"test.verifiers"(%0) ({
%1 = arith.constant 2 : index
}) : (index) -> ()
return
}
// -----
func @verify_nested_op_block_trait() {
%0 = arith.constant 1 : i32
// expected-remark@+1 {{success run of verifier}}
"test.verifiers"(%0) ({
%1 = arith.constant 2 : index
// expected-error@+1 {{op requires one region}}
"test.verifiers"(%1) : (index) -> ()
}) : (i32) -> ()
return
}
// -----
func @verify_nested_op_operand() {
%0 = arith.constant 1 : i32
// expected-remark@+1 {{success run of verifier}}
"test.verifiers"(%0) ({
%1 = arith.constant 2 : index
// expected-error@+1 {{op operand #0 must be 32-bit signless integer, but got 'index'}}
"test.verifiers"(%1) ({
%2 = arith.constant 3 : index
}) : (index) -> ()
}) : (i32) -> ()
return
}
// -----
func @verify_nested_isolated_above() {
%0 = arith.constant 1 : i32
// expected-remark@+1 {{success run of verifier}}
"test.verifiers"(%0) ({
// expected-remark@-1 {{success run of region verifier}}
%1 = arith.constant 2 : i32
// expected-remark@+1 {{success run of verifier}}
"test.verifiers"(%1) ({
// expected-remark@-1 {{success run of region verifier}}
%2 = arith.constant 3 : index
}) : (i32) -> ()
}) : (i32) -> ()
return
}

View File

@ -18,6 +18,7 @@
#include "mlir/IR/DialectImplementation.h"
#include "mlir/IR/PatternMatch.h"
#include "mlir/IR/TypeUtilities.h"
#include "mlir/IR/Verifier.h"
#include "mlir/Reducer/ReductionPatternInterface.h"
#include "mlir/Transforms/FoldUtils.h"
#include "mlir/Transforms/InliningUtils.h"
@ -1305,6 +1306,37 @@ void SingleNoTerminatorCustomAsmOp::print(OpAsmPrinter &printer) {
/*printBlockTerminators=*/false);
}
//===----------------------------------------------------------------------===//
// TestVerifiersOp
//===----------------------------------------------------------------------===//
LogicalResult TestVerifiersOp::verify() {
if (!getRegion().hasOneBlock())
return emitOpError("`hasOneBlock` trait hasn't been verified");
Operation *definingOp = getInput().getDefiningOp();
if (definingOp && failed(mlir::verify(definingOp)))
return emitOpError("operand hasn't been verified");
emitRemark("success run of verifier");
return success();
}
LogicalResult TestVerifiersOp::verifyRegions() {
if (!getRegion().hasOneBlock())
return emitOpError("`hasOneBlock` trait hasn't been verified");
for (Block &block : getRegion())
for (Operation &op : block)
if (failed(mlir::verify(&op)))
return emitOpError("nested op hasn't been verified");
emitRemark("success run of region verifier");
return success();
}
#include "TestOpEnums.cpp.inc"
#include "TestOpInterfaces.cpp.inc"
#include "TestOpStructs.cpp.inc"

View File

@ -2780,4 +2780,16 @@ def TestEffectsRead : TEST_Op<"op_with_memread",
def TestEffectsWrite : TEST_Op<"op_with_memwrite",
[MemoryEffects<[MemWrite]>]>;
//===----------------------------------------------------------------------===//
// Test Ops with verifiers
//===----------------------------------------------------------------------===//
def TestVerifiersOp : TEST_Op<"verifiers",
[SingleBlock, NoTerminator, IsolatedFromAbove]> {
let arguments = (ins I32:$input);
let regions = (region SizedRegion<1>:$region);
let hasVerifier = 1;
let hasRegionVerifier = 1;
}
#endif // TEST_OPS