34#include "llvm/ADT/PointerIntPair.h"
41class OperationVerifier {
45 explicit OperationVerifier(
bool verifyRecursively)
46 : verifyRecursively(verifyRecursively) {}
49 LogicalResult verifyOpAndDominance(Operation &op);
52 using WorkItem = llvm::PointerUnion<Operation *, Block *>;
53 using WorkItemEntry = llvm::PointerIntPair<WorkItem, 1, bool>;
59 LogicalResult verifyOnEntrance(
Block &block);
60 LogicalResult verifyOnEntrance(Operation &op);
62 LogicalResult verifyOnExit(
Block &block);
63 LogicalResult verifyOnExit(Operation &op);
66 LogicalResult verifyOperation(Operation &op);
70 LogicalResult verifyDominanceOfContainedRegions(Operation &op,
71 DominanceInfo &domInfo);
75 bool verifyRecursively;
79LogicalResult OperationVerifier::verifyOpAndDominance(
Operation &op) {
81 if (
failed(verifyOperation(op)))
89 DominanceInfo domInfo;
90 if (
failed(verifyDominanceOfContainedRegions(op, domInfo)))
106 if (!llvm::hasSingleElement(*block->
getParent()))
112LogicalResult OperationVerifier::verifyOnEntrance(
Block &block) {
116 MLIRContext *blockCtx = parentOp ? parentOp->
getContext() :
nullptr;
118 for (
auto [idx, arg] : llvm::enumerate(block.
getArguments())) {
119 if (arg.getOwner() != &block)
120 return emitError(arg.getLoc(),
"block argument not owned by block");
125 if (arg.getLoc().getContext() != blockCtx)
128 <<
" location from a different MLIRContext than its "
130 if (arg.getType().getContext() != blockCtx)
131 return emitError(arg.getLoc(),
"block argument #")
133 <<
" type from a different MLIRContext than its "
143 "empty block: expect at least a terminator");
148 for (Operation &op : block) {
152 "operation with block successors must terminate its parent block");
157 if (blockCtx && op.
getContext() != blockCtx) {
160 <<
"' has a location from a different MLIRContext than its "
169LogicalResult OperationVerifier::verifyOnExit(
Block &block) {
173 if (successor->getParent() != block.
getParent())
175 "branching to block of a different region");
181 Operation &terminator = block.
back();
183 return block.
back().
emitError(
"block with no terminator, has ")
189LogicalResult OperationVerifier::verifyOnEntrance(Operation &op) {
196 "operation name from a different MLIRContext than this operation");
200 if (
result.getType().getContext() != opCtx)
203 <<
" type from a different MLIRContext than this operation";
207 for (
auto [i, operand] : llvm::enumerate(op.
getOperands())) {
209 return op.
emitError(
"null operand found");
210 if (operand.getType().getContext() != opCtx)
213 <<
" type from a different MLIRContext than this operation";
218 if (attr.getValue().getContext() != opCtx)
220 <<
"discardable attribute '" << attr.getName()
221 <<
"' value from a different MLIRContext than this operation";
223 if (
auto *dialect = attr.getNameDialect())
224 if (
failed(dialect->verifyOperationAttribute(&op, attr)))
229 OperationName opName = op.
getName();
230 std::optional<RegisteredOperationName> registeredInfo =
232 if (registeredInfo &&
failed(registeredInfo->verifyInvariants(&op)))
238 auto kindInterface = dyn_cast<RegionKindInterface>(&op);
240 MutableArrayRef<Region> regions = op.
getRegions();
241 for (
unsigned i = 0; i < numRegions; ++i) {
242 Region ®ion = regions[i];
244 kindInterface ? kindInterface.getRegionKind(i) : RegionKind::SSACFG;
254 << i <<
" to have 0 or 1 blocks";
264 "entry block of region may not have predecessors");
269LogicalResult OperationVerifier::verifyOnExit(Operation &op) {
270 SmallVector<Operation *> opsWithIsolatedRegions;
271 if (verifyRecursively) {
273 for (
Block &block : region)
274 for (Operation &o : block)
275 if (o.getNumRegions() != 0 &&
276 o.hasTrait<OpTrait::IsIsolatedFromAbove>())
277 opsWithIsolatedRegions.push_back(&o);
280 std::atomic<bool> opFailedVerify =
false;
282 if (failed(verifyOpAndDominance(*o)))
283 opFailedVerify.store(true, std::memory_order_relaxed);
285 if (opFailedVerify.load(std::memory_order_relaxed))
288 OperationName opName = op.
getName();
289 std::optional<RegisteredOperationName> registeredInfo =
293 if (registeredInfo &&
failed(registeredInfo->verifyRegionInvariants(&op)))
305 <<
"created with unregistered dialect. If this is "
306 "intended, please call allowUnregisteredDialects() on the "
307 "MLIRContext, or use -allow-unregistered-dialect with "
308 "the MLIR opt tool used";
314 return op.
emitError(
"unregistered operation '")
316 <<
"') that does not allow unknown operations";
326LogicalResult OperationVerifier::verifyOperation(Operation &op) {
327 SmallVector<WorkItemEntry> worklist{{&op,
false}};
328 while (!worklist.empty()) {
329 WorkItemEntry &top = worklist.back();
331 auto visit = [](
auto &&visitor, WorkItem w) {
332 if (
auto *o = dyn_cast<Operation *>(w))
334 return visitor(cast<Block *>(w));
337 const bool isExit = top.getInt();
339 auto item = top.getPointer();
344 visit([
this](
auto *workItem) {
return verifyOnExit(*workItem); },
353 [
this](
auto *workItem) {
return verifyOnEntrance(*workItem); },
357 if (
Block *currentBlock = dyn_cast<Block *>(item)) {
359 for (Operation &o : llvm::reverse(*currentBlock)) {
360 if (o.getNumRegions() == 0 ||
361 !o.hasTrait<OpTrait::IsIsolatedFromAbove>())
362 worklist.emplace_back(&o);
367 Operation ¤tOp = *cast<Operation *>(item);
368 if (verifyRecursively)
369 for (Region ®ion : llvm::reverse(currentOp.
getRegions()))
370 for (
Block &block : llvm::reverse(region))
371 worklist.emplace_back(&block);
384 << operandNo <<
" does not dominate this use";
392 note <<
"operand defined here";
394 Block *block2 = useOp->getBlock();
397 if (block1 == block2)
398 note <<
" (op in the same block)";
399 else if (region1 == region2)
400 note <<
" (op in the same region)";
402 note <<
" (op in a parent region)";
404 note <<
" (op in a child region)";
406 note <<
" (op is neither in a parent nor in a child region)";
411 Block *block2 = llvm::cast<BlockArgument>(operand).getOwner();
419 note <<
" (block without parent)";
422 if (block1 == block2)
423 llvm::report_fatal_error(
"Internal error in dominance verification");
425 note <<
"operand defined as a block argument (block #" <<
index;
426 if (region1 == region2)
427 note <<
" in the same region)";
429 note <<
" in a parent region)";
431 note <<
" in a child region)";
433 note <<
" neither in a parent nor in a child region)";
438OperationVerifier::verifyDominanceOfContainedRegions(Operation &op,
439 DominanceInfo &domInfo) {
440 llvm::SmallVector<Operation *, 8> worklist{&op};
441 while (!worklist.empty()) {
442 auto *op = worklist.pop_back_val();
444 for (
auto &block : region.getBlocks()) {
447 for (
auto &op : block) {
450 for (
const auto &operand : llvm::enumerate(op.
getOperands())) {
465 if (op.
hasTrait<OpTrait::IsIsolatedFromAbove>())
467 worklist.push_back(&op);
481 OperationVerifier verifier(verifyRecursively);
482 return verifier.verifyOpAndDominance(*op);
static void visit(Operation *op, DenseSet< Operation * > &visited)
Visits all the pdl.operand(s), pdl.result(s), and pdl.operation(s) connected to the given operation.
static std::string diag(const llvm::Value &value)
static bool mayBeValidWithoutTerminator(Block *block)
Returns true if this block may be valid without terminator.
static void diagnoseInvalidOperandDominance(Operation &op, unsigned operandNo)
Emit an error when the specified operand of the specified operation is an invalid use because of domi...
Block represents an ordered list of Operations.
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
SuccessorRange getSuccessors()
BlockArgListType getArguments()
bool hasNoPredecessors()
Return true if this block has no predecessors.
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
unsigned computeBlockNumber()
Compute the position of this block within its parent region using an O(N) linear scan.
This class contains all of the information necessary to report a diagnostic to the DiagnosticEngine.
StringRef getNamespace() const
bool allowsUnknownOperations() const
Returns true if this dialect allows for unregistered operations, i.e.
bool properlyDominates(Operation *a, Operation *b, bool enclosingOpOk=true) const
Return true if operation A properly dominates operation B, i.e.
This class represents a diagnostic that is inflight and set to be reported.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
bool allowsUnregisteredDialects()
Return true if we allow to create operation for unregistered dialects.
This class indicates that the regions associated with this op don't have terminators.
Dialect * getDialect() const
Return the dialect this operation is registered to if the dialect is loaded in the context,...
std::optional< RegisteredOperationName > getRegisteredInfo() const
If this operation is registered, returns the registered information, std::nullopt otherwise.
MLIRContext * getContext()
Return the context this operation is associated with.
Operation is the basic unit of execution within MLIR.
Value getOperand(unsigned idx)
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
unsigned getNumSuccessors()
bool isRegistered()
Returns true if this operation has a registered operation description, otherwise false.
bool mightHaveTrait()
Returns true if the operation might have the provided trait.
Block * getBlock()
Returns the operation block that contains this operation.
unsigned getNumRegions()
Returns the number of regions held by this operation.
Location getLoc()
The source location the operation was defined or derived from.
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
OperationName getName()
The name of an operation is the key identifier for it.
DictionaryAttr getDiscardableAttrDictionary()
Return all of the discardable attributes on this operation as a DictionaryAttr.
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
operand_range getOperands()
Returns an iterator on the underlying Value's.
result_range getResults()
MLIRContext * getContext()
Return the context this operation is associated with.
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
bool isProperAncestor(Region *other)
Return true if this region is a proper ancestor of the other region.
Location getLoc()
Return a location for this region.
bool hasOneBlock()
Return true if this region has exactly one block.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
bool isReachableFromEntry(Block *a) const
Return true if the specified block is reachable from the entry block of its region.
Include the generated interface declarations.
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
void parallelForEach(MLIRContext *context, IteratorT begin, IteratorT end, FuncT &&func)
Invoke the given function on the elements between [begin, end) asynchronously.
RegionKind
The kinds of regions contained in an operation.
LogicalResult verify(Operation *op, bool verifyRecursively=true)
Perform (potentially expensive) checks of invariants, used to detect compiler bugs,...