35#include "llvm/ADT/PointerIntPair.h"
42class OperationVerifier {
46 OperationVerifier(MLIRContext *ctx,
bool verifyRecursively)
47 : tokenType(TokenType::
get(ctx)), verifyRecursively(verifyRecursively) {}
50 LogicalResult verifyOpAndDominance(Operation &op);
53 using WorkItem = llvm::PointerUnion<Operation *, Block *>;
54 using WorkItemEntry = llvm::PointerIntPair<WorkItem, 1, bool>;
60 LogicalResult verifyOnEntrance(
Block &block);
61 LogicalResult verifyOnEntrance(Operation &op);
63 verifyTokenValue(Operation &producer, Value value,
65 LogicalResult verifyTokenValues(Operation &op);
66 LogicalResult verifyTokenBlockArgument(
Block &block, BlockArgument arg,
69 LogicalResult verifyOnExit(
Block &block);
70 LogicalResult verifyOnExit(Operation &op);
73 LogicalResult verifyOperation(Operation &op);
77 LogicalResult verifyDominanceOfContainedRegions(Operation &op,
78 DominanceInfo &domInfo);
85 bool verifyRecursively;
89LogicalResult OperationVerifier::verifyOpAndDominance(
Operation &op) {
91 if (
failed(verifyOperation(op)))
99 DominanceInfo domInfo;
100 if (
failed(verifyDominanceOfContainedRegions(op, domInfo)))
116 if (!llvm::hasSingleElement(*block->
getParent()))
122LogicalResult OperationVerifier::verifyTokenValue(
123 Operation &producer, Value value,
125 if (value.
getType() != tokenType)
129 return emitProducerError();
131 for (OpOperand &use : value.
getUses()) {
132 Operation *user = use.getOwner();
137 <<
"consumes token operand #" << use.getOperandNumber()
138 <<
" but does not have the TokenConsumerTrait";
144LogicalResult OperationVerifier::verifyTokenValues(Operation &op) {
145 for (
auto resultIt : llvm::enumerate(op.
getResults())) {
146 unsigned idx = resultIt.index();
147 OpResult
result = resultIt.value();
150 <<
"produces token result #" << idx
151 <<
" but does not have the TokenProducerTrait";
160 Block &entryBlock = region.front();
161 for (
auto argIt : llvm::enumerate(entryBlock.
getArguments())) {
162 unsigned idx = argIt.index();
163 BlockArgument arg = argIt.value();
164 if (
failed(verifyTokenValue(op, arg, [&]() {
166 << idx <<
" requires the parent operation to have the "
167 <<
"TokenProducerTrait";
176LogicalResult OperationVerifier::verifyTokenBlockArgument(
Block &block,
179 if (arg.
getType() != tokenType)
188 << idx <<
" is only allowed in a region entry block";
193LogicalResult OperationVerifier::verifyOnEntrance(
Block &block) {
197 MLIRContext *blockCtx = parentOp ? parentOp->
getContext() :
nullptr;
199 for (
auto [idx, arg] : llvm::enumerate(block.
getArguments())) {
209 <<
" location from a different MLIRContext than its "
214 <<
" type from a different MLIRContext than its "
217 if (
failed(verifyTokenBlockArgument(block, arg, idx)))
226 "empty block: expect at least a terminator");
231 for (Operation &op : block) {
235 "operation with block successors must terminate its parent block");
240 if (blockCtx && op.
getContext() != blockCtx) {
243 <<
"' has a location from a different MLIRContext than its "
252LogicalResult OperationVerifier::verifyOnExit(
Block &block) {
256 if (successor->getParent() != block.
getParent())
258 "branching to block of a different region");
264 Operation &terminator = block.
back();
266 return block.
back().
emitError(
"block with no terminator, has ")
272LogicalResult OperationVerifier::verifyOnEntrance(Operation &op) {
279 "operation name from a different MLIRContext than this operation");
283 if (
result.getType().getContext() != opCtx)
286 <<
" type from a different MLIRContext than this operation";
290 for (
auto [i, operand] : llvm::enumerate(op.
getOperands())) {
292 return op.
emitError(
"null operand found");
293 if (operand.getType().getContext() != opCtx)
296 <<
" type from a different MLIRContext than this operation";
301 if (attr.getValue().getContext() != opCtx)
303 <<
"discardable attribute '" << attr.getName()
304 <<
"' value from a different MLIRContext than this operation";
306 if (
auto *dialect = attr.getNameDialect())
307 if (
failed(dialect->verifyOperationAttribute(&op, attr)))
312 OperationName opName = op.
getName();
313 std::optional<RegisteredOperationName> registeredInfo =
315 if (registeredInfo &&
failed(registeredInfo->verifyInvariants(&op)))
318 if (
failed(verifyTokenValues(op)))
324 auto kindInterface = dyn_cast<RegionKindInterface>(&op);
326 MutableArrayRef<Region> regions = op.
getRegions();
327 for (
unsigned i = 0; i < numRegions; ++i) {
328 Region ®ion = regions[i];
330 kindInterface ? kindInterface.getRegionKind(i) : RegionKind::SSACFG;
340 << i <<
" to have 0 or 1 blocks";
350 "entry block of region may not have predecessors");
355LogicalResult OperationVerifier::verifyOnExit(Operation &op) {
356 SmallVector<Operation *> opsWithIsolatedRegions;
357 if (verifyRecursively) {
359 for (
Block &block : region)
360 for (Operation &o : block)
361 if (o.getNumRegions() != 0 &&
362 o.hasTrait<OpTrait::IsIsolatedFromAbove>())
363 opsWithIsolatedRegions.push_back(&o);
366 std::atomic<bool> opFailedVerify =
false;
368 if (failed(verifyOpAndDominance(*o)))
369 opFailedVerify.store(true, std::memory_order_relaxed);
371 if (opFailedVerify.load(std::memory_order_relaxed))
374 OperationName opName = op.
getName();
375 std::optional<RegisteredOperationName> registeredInfo =
379 if (registeredInfo &&
failed(registeredInfo->verifyRegionInvariants(&op)))
391 <<
"created with unregistered dialect. If this is "
392 "intended, please call allowUnregisteredDialects() on the "
393 "MLIRContext, or use -allow-unregistered-dialect with "
394 "the MLIR opt tool used";
400 return op.
emitError(
"unregistered operation '")
402 <<
"') that does not allow unknown operations";
412LogicalResult OperationVerifier::verifyOperation(Operation &op) {
413 SmallVector<WorkItemEntry> worklist{{&op,
false}};
414 while (!worklist.empty()) {
415 WorkItemEntry &top = worklist.back();
417 auto visit = [](
auto &&visitor, WorkItem w) {
418 if (
auto *o = dyn_cast<Operation *>(w))
420 return visitor(cast<Block *>(w));
423 const bool isExit = top.getInt();
425 auto item = top.getPointer();
430 visit([
this](
auto *workItem) {
return verifyOnExit(*workItem); },
439 [
this](
auto *workItem) {
return verifyOnEntrance(*workItem); },
443 if (
Block *currentBlock = dyn_cast<Block *>(item)) {
445 for (Operation &o : llvm::reverse(*currentBlock)) {
446 if (o.getNumRegions() == 0 ||
447 !o.hasTrait<OpTrait::IsIsolatedFromAbove>())
448 worklist.emplace_back(&o);
453 Operation ¤tOp = *cast<Operation *>(item);
454 if (verifyRecursively)
455 for (Region ®ion : llvm::reverse(currentOp.
getRegions()))
456 for (
Block &block : llvm::reverse(region))
457 worklist.emplace_back(&block);
470 << operandNo <<
" does not dominate this use";
478 note <<
"operand defined here";
480 Block *block2 = useOp->getBlock();
483 if (block1 == block2)
484 note <<
" (op in the same block)";
485 else if (region1 == region2)
486 note <<
" (op in the same region)";
488 note <<
" (op in a parent region)";
490 note <<
" (op in a child region)";
492 note <<
" (op is neither in a parent nor in a child region)";
497 Block *block2 = llvm::cast<BlockArgument>(operand).getOwner();
505 note <<
" (block without parent)";
508 if (block1 == block2)
509 llvm::report_fatal_error(
"Internal error in dominance verification");
511 note <<
"operand defined as a block argument (block #" <<
index;
512 if (region1 == region2)
513 note <<
" in the same region)";
515 note <<
" in a parent region)";
517 note <<
" in a child region)";
519 note <<
" neither in a parent nor in a child region)";
524OperationVerifier::verifyDominanceOfContainedRegions(Operation &op,
525 DominanceInfo &domInfo) {
526 llvm::SmallVector<Operation *, 8> worklist{&op};
527 while (!worklist.empty()) {
528 auto *op = worklist.pop_back_val();
530 for (
auto &block : region.getBlocks()) {
533 for (
auto &op : block) {
536 for (
const auto &operand : llvm::enumerate(op.
getOperands())) {
551 if (op.
hasTrait<OpTrait::IsIsolatedFromAbove>())
553 worklist.push_back(&op);
567 OperationVerifier verifier(op->
getContext(), verifyRecursively);
568 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...
Location getLoc() const
Return the location for this argument.
Block * getOwner() const
Returns the block that owns this argument.
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 isEntryBlock()
Return if this block is the entry block in the parent region.
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...
MLIRContext * getContext() const
Return the context this location is uniqued in.
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.
MLIRContext * getContext() const
Return the MLIRContext in which this type was uniqued.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
use_range getUses() const
Returns a range of all uses, which is useful for iterating over all uses.
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.
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
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,...
llvm::function_ref< Fn > function_ref