MLIR 23.0.0git
Verifier.cpp
Go to the documentation of this file.
1//===- Verifier.cpp - MLIR Verifier Implementation ------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements the verify() methods on the various IR types, performing
10// (potentially expensive) checks on the holistic structure of the code. This
11// can be used for detecting bugs in compiler transformations and hand written
12// .mlir files.
13//
14// The checks in this file are only for things that can occur as part of IR
15// transformations: e.g. violation of dominance information, malformed operation
16// attributes, etc. MLIR supports transformations moving IR through locally
17// invalid states (e.g. unlinking an operation from a block before re-inserting
18// it in a new place), but each transformation must complete with the IR in a
19// valid form.
20//
21// This should not check for things that are always wrong by construction (e.g.
22// attributes or other immutable structures that are incorrect), because those
23// are not mutable and can be checked at time of construction.
24//
25//===----------------------------------------------------------------------===//
26
27#include "mlir/IR/Verifier.h"
28#include "mlir/IR/Attributes.h"
29#include "mlir/IR/Dialect.h"
30#include "mlir/IR/Dominance.h"
31#include "mlir/IR/Operation.h"
33#include "mlir/IR/Threading.h"
34#include "llvm/ADT/PointerIntPair.h"
35#include <optional>
36
37using namespace mlir;
38
39namespace {
40/// This class encapsulates all the state used to verify an operation region.
41class OperationVerifier {
42public:
43 /// If `verifyRecursively` is true, then this will also recursively verify
44 /// nested operations.
45 explicit OperationVerifier(bool verifyRecursively)
46 : verifyRecursively(verifyRecursively) {}
47
48 /// Verify the given operation.
49 LogicalResult verifyOpAndDominance(Operation &op);
50
51private:
52 using WorkItem = llvm::PointerUnion<Operation *, Block *>;
53 using WorkItemEntry = llvm::PointerIntPair<WorkItem, 1, bool>;
54
55 /// This verifier uses a DFS of the tree of operations/blocks. The method
56 /// verifyOnEntrance is invoked when we visit a node for the first time, i.e.
57 /// before visiting its children. The method verifyOnExit is invoked
58 /// upon exit from the subtree, i.e. when we visit a node for the second time.
59 LogicalResult verifyOnEntrance(Block &block);
60 LogicalResult verifyOnEntrance(Operation &op);
61
62 LogicalResult verifyOnExit(Block &block);
63 LogicalResult verifyOnExit(Operation &op);
64
65 /// Verify the properties and dominance relationships of this operation.
66 LogicalResult verifyOperation(Operation &op);
67
68 /// Verify the dominance property of regions contained within the given
69 /// Operation.
70 LogicalResult verifyDominanceOfContainedRegions(Operation &op,
71 DominanceInfo &domInfo);
72
73 /// A flag indicating if this verifier should recursively verify nested
74 /// operations.
75 bool verifyRecursively;
76};
77} // namespace
78
79LogicalResult OperationVerifier::verifyOpAndDominance(Operation &op) {
80 // Verify the operation first, collecting any IsolatedFromAbove operations.
81 if (failed(verifyOperation(op)))
82 return failure();
83
84 // Since everything looks structurally ok to this point, we do a dominance
85 // check for any nested regions. We do this as a second pass since malformed
86 // CFG's can cause dominator analysis construction to crash and we want the
87 // verifier to be resilient to malformed code.
88 if (op.getNumRegions() != 0) {
89 DominanceInfo domInfo;
90 if (failed(verifyDominanceOfContainedRegions(op, domInfo)))
91 return failure();
92 }
93
94 return success();
95}
96
97/// Returns true if this block may be valid without terminator. That is if:
98/// - it does not have a parent region.
99/// - Or the parent region have a single block and:
100/// - This region does not have a parent op.
101/// - Or the parent op is unregistered.
102/// - Or the parent op has the NoTerminator trait.
104 if (!block->getParent())
105 return true;
106 if (!llvm::hasSingleElement(*block->getParent()))
107 return false;
108 Operation *op = block->getParentOp();
109 return !op || op->mightHaveTrait<OpTrait::NoTerminator>();
110}
111
112LogicalResult OperationVerifier::verifyOnEntrance(Block &block) {
113 // Get the parent op and context for cross-context checks. Both are available
114 // whenever the block lives inside a region that has a parent operation.
115 Operation *parentOp = block.getParentOp();
116 MLIRContext *blockCtx = parentOp ? parentOp->getContext() : nullptr;
117
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");
121 if (blockCtx) {
122 // Check the location first; if it is wrong we must use the parent op's
123 // location to emit the error (the arg location would route to the wrong
124 // context's diagnostic handler).
125 if (arg.getLoc().getContext() != blockCtx)
126 return emitError(parentOp->getLoc(), "block argument #")
127 << idx
128 << " location from a different MLIRContext than its "
129 "parent operation";
130 if (arg.getType().getContext() != blockCtx)
131 return emitError(arg.getLoc(), "block argument #")
132 << idx
133 << " type from a different MLIRContext than its "
134 "parent operation";
135 }
136 }
137
138 // Verify that this block has a terminator.
139 if (block.empty()) {
140 if (mayBeValidWithoutTerminator(&block))
141 return success();
142 return emitError(block.getParent()->getLoc(),
143 "empty block: expect at least a terminator");
144 }
145
146 // Check each operation, and make sure there are no branches out of the
147 // middle of this block.
148 for (Operation &op : block) {
149 // Only the last instructions is allowed to have successors.
150 if (op.getNumSuccessors() != 0 && &op != &block.back())
151 return op.emitError(
152 "operation with block successors must terminate its parent block");
153 // Check that each op's location (which defines its context) is from the
154 // same MLIRContext as the enclosing block. We cannot use op.emitError()
155 // here because op's context is the wrong one; emit via the parent op's
156 // location instead.
157 if (blockCtx && op.getContext() != blockCtx) {
158 emitError(parentOp->getLoc(), "operation '")
159 << op.getName()
160 << "' has a location from a different MLIRContext than its "
161 "enclosing block";
162 return failure();
163 }
164 }
165
166 return success();
167}
168
169LogicalResult OperationVerifier::verifyOnExit(Block &block) {
170 // Verify that this block is not branching to a block of a different
171 // region.
172 for (Block *successor : block.getSuccessors())
173 if (successor->getParent() != block.getParent())
174 return block.back().emitOpError(
175 "branching to block of a different region");
176
177 // If this block doesn't have to have a terminator, don't require it.
178 if (mayBeValidWithoutTerminator(&block))
179 return success();
180
181 Operation &terminator = block.back();
182 if (!terminator.mightHaveTrait<OpTrait::IsTerminator>())
183 return block.back().emitError("block with no terminator, has ")
184 << terminator;
185
186 return success();
187}
188
189LogicalResult OperationVerifier::verifyOnEntrance(Operation &op) {
190 // op.getContext() is defined as location->getContext(), so opCtx is the
191 // location's context by construction. The OperationName, however, carries
192 // its own context reference and can independently point elsewhere.
193 MLIRContext *opCtx = op.getContext();
194 if (op.getName().getContext() != opCtx)
195 return op.emitError(
196 "operation name from a different MLIRContext than this operation");
197
198 // Check result types are from the same context as the operation.
199 for (auto [i, result] : llvm::enumerate(op.getResults())) {
200 if (result.getType().getContext() != opCtx)
201 return op.emitOpError()
202 << "result #" << i
203 << " type from a different MLIRContext than this operation";
204 }
205
206 // Check that operands are non-nil and their types are from the same context.
207 for (auto [i, operand] : llvm::enumerate(op.getOperands())) {
208 if (!operand)
209 return op.emitError("null operand found");
210 if (operand.getType().getContext() != opCtx)
211 return op.emitOpError()
212 << "operand #" << i
213 << " type from a different MLIRContext than this operation";
214 }
215
216 /// Verify that all of the attributes are okay.
217 for (auto attr : op.getDiscardableAttrDictionary()) {
218 if (attr.getValue().getContext() != opCtx)
219 return op.emitOpError()
220 << "discardable attribute '" << attr.getName()
221 << "' value from a different MLIRContext than this operation";
222 // Check for any optional dialect specific attributes.
223 if (auto *dialect = attr.getNameDialect())
224 if (failed(dialect->verifyOperationAttribute(&op, attr)))
225 return failure();
226 }
227
228 // If we can get operation info for this, check the custom hook.
229 OperationName opName = op.getName();
230 std::optional<RegisteredOperationName> registeredInfo =
231 opName.getRegisteredInfo();
232 if (registeredInfo && failed(registeredInfo->verifyInvariants(&op)))
233 return failure();
234
235 unsigned numRegions = op.getNumRegions();
236 if (!numRegions)
237 return success();
238 auto kindInterface = dyn_cast<RegionKindInterface>(&op);
239 // Verify that all child regions are ok.
240 MutableArrayRef<Region> regions = op.getRegions();
241 for (unsigned i = 0; i < numRegions; ++i) {
242 Region &region = regions[i];
243 RegionKind kind =
244 kindInterface ? kindInterface.getRegionKind(i) : RegionKind::SSACFG;
245 // Check that Graph Regions only have a single basic block. This is
246 // similar to the code in SingleBlockImplicitTerminator, but doesn't
247 // require the trait to be specified. This arbitrary limitation is
248 // designed to limit the number of cases that have to be handled by
249 // transforms and conversions.
250 if (op.isRegistered() && kind == RegionKind::Graph) {
251 // Non-empty regions must contain a single basic block.
252 if (!region.empty() && !region.hasOneBlock())
253 return op.emitOpError("expects graph region #")
254 << i << " to have 0 or 1 blocks";
255 }
256
257 if (region.empty())
258 continue;
259
260 // Verify the first block has no predecessors.
261 Block *firstBB = &region.front();
262 if (!firstBB->hasNoPredecessors())
263 return emitError(op.getLoc(),
264 "entry block of region may not have predecessors");
265 }
266 return success();
267}
268
269LogicalResult OperationVerifier::verifyOnExit(Operation &op) {
270 SmallVector<Operation *> opsWithIsolatedRegions;
271 if (verifyRecursively) {
272 for (Region &region : op.getRegions())
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);
278 }
279
280 std::atomic<bool> opFailedVerify = false;
281 parallelForEach(op.getContext(), opsWithIsolatedRegions, [&](Operation *o) {
282 if (failed(verifyOpAndDominance(*o)))
283 opFailedVerify.store(true, std::memory_order_relaxed);
284 });
285 if (opFailedVerify.load(std::memory_order_relaxed))
286 return failure();
287
288 OperationName opName = op.getName();
289 std::optional<RegisteredOperationName> registeredInfo =
290 opName.getRegisteredInfo();
291 // After the region ops are verified, run the verifiers that have additional
292 // region invariants need to veirfy.
293 if (registeredInfo && failed(registeredInfo->verifyRegionInvariants(&op)))
294 return failure();
295
296 // If this is a registered operation, there is nothing left to do.
297 if (registeredInfo)
298 return success();
299
300 // Otherwise, verify that the parent dialect allows un-registered operations.
301 Dialect *dialect = opName.getDialect();
302 if (!dialect) {
304 return op.emitOpError()
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";
309 }
310 return success();
311 }
312
313 if (!dialect->allowsUnknownOperations()) {
314 return op.emitError("unregistered operation '")
315 << op.getName() << "' found in dialect ('" << dialect->getNamespace()
316 << "') that does not allow unknown operations";
317 }
318
319 return success();
320}
321
322/// Verify the properties and dominance relationships of this operation,
323/// stopping region "recursion" at any "isolated from above operations".
324/// Such ops are collected separately and verified inside
325/// verifyBlockPostChildren.
326LogicalResult OperationVerifier::verifyOperation(Operation &op) {
327 SmallVector<WorkItemEntry> worklist{{&op, false}};
328 while (!worklist.empty()) {
329 WorkItemEntry &top = worklist.back();
330
331 auto visit = [](auto &&visitor, WorkItem w) {
332 if (auto *o = dyn_cast<Operation *>(w))
333 return visitor(o);
334 return visitor(cast<Block *>(w));
335 };
336
337 const bool isExit = top.getInt();
338 top.setInt(true);
339 auto item = top.getPointer();
340
341 // 2nd visit of this work item ("exit").
342 if (isExit) {
343 if (failed(
344 visit([this](auto *workItem) { return verifyOnExit(*workItem); },
345 item)))
346 return failure();
347 worklist.pop_back();
348 continue;
349 }
350
351 // 1st visit of this work item ("entrance").
352 if (failed(visit(
353 [this](auto *workItem) { return verifyOnEntrance(*workItem); },
354 item)))
355 return failure();
356
357 if (Block *currentBlock = dyn_cast<Block *>(item)) {
358 // Skip "isolated from above operations".
359 for (Operation &o : llvm::reverse(*currentBlock)) {
360 if (o.getNumRegions() == 0 ||
361 !o.hasTrait<OpTrait::IsIsolatedFromAbove>())
362 worklist.emplace_back(&o);
363 }
364 continue;
365 }
366
367 Operation &currentOp = *cast<Operation *>(item);
368 if (verifyRecursively)
369 for (Region &region : llvm::reverse(currentOp.getRegions()))
370 for (Block &block : llvm::reverse(region))
371 worklist.emplace_back(&block);
372 }
373 return success();
374}
375
376//===----------------------------------------------------------------------===//
377// Dominance Checking
378//===----------------------------------------------------------------------===//
379
380/// Emit an error when the specified operand of the specified operation is an
381/// invalid use because of dominance properties.
382static void diagnoseInvalidOperandDominance(Operation &op, unsigned operandNo) {
383 InFlightDiagnostic diag = op.emitError("operand #")
384 << operandNo << " does not dominate this use";
385
386 Value operand = op.getOperand(operandNo);
387
388 /// Attach a note to an in-flight diagnostic that provide more information
389 /// about where an op operand is defined.
390 if (auto *useOp = operand.getDefiningOp()) {
391 Diagnostic &note = diag.attachNote(useOp->getLoc());
392 note << "operand defined here";
393 Block *block1 = op.getBlock();
394 Block *block2 = useOp->getBlock();
395 Region *region1 = block1->getParent();
396 Region *region2 = block2->getParent();
397 if (block1 == block2)
398 note << " (op in the same block)";
399 else if (region1 == region2)
400 note << " (op in the same region)";
401 else if (region2->isProperAncestor(region1))
402 note << " (op in a parent region)";
403 else if (region1->isProperAncestor(region2))
404 note << " (op in a child region)";
405 else
406 note << " (op is neither in a parent nor in a child region)";
407 return;
408 }
409 // Block argument case.
410 Block *block1 = op.getBlock();
411 Block *block2 = llvm::cast<BlockArgument>(operand).getOwner();
412 Region *region1 = block1->getParent();
413 Region *region2 = block2->getParent();
414 Location loc = UnknownLoc::get(op.getContext());
415 if (block2->getParentOp())
416 loc = block2->getParentOp()->getLoc();
417 Diagnostic &note = diag.attachNote(loc);
418 if (!region2) {
419 note << " (block without parent)";
420 return;
421 }
422 if (block1 == block2)
423 llvm::report_fatal_error("Internal error in dominance verification");
424 unsigned index = block2->computeBlockNumber();
425 note << "operand defined as a block argument (block #" << index;
426 if (region1 == region2)
427 note << " in the same region)";
428 else if (region2->isProperAncestor(region1))
429 note << " in a parent region)";
430 else if (region1->isProperAncestor(region2))
431 note << " in a child region)";
432 else
433 note << " neither in a parent nor in a child region)";
434}
435
436/// Verify the dominance of each of the nested blocks within the given operation
437LogicalResult
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();
443 for (auto &region : op->getRegions())
444 for (auto &block : region.getBlocks()) {
445 // Dominance is only meaningful inside reachable blocks.
446 bool isReachable = domInfo.isReachableFromEntry(&block);
447 for (auto &op : block) {
448 if (isReachable) {
449 // Check that operands properly dominate this use.
450 for (const auto &operand : llvm::enumerate(op.getOperands())) {
451 if (domInfo.properlyDominates(operand.value(), &op))
452 continue;
453
454 diagnoseInvalidOperandDominance(op, operand.index());
455 return failure();
456 }
457 }
458
459 // Recursively verify dominance within each operation in the block,
460 // even if the block itself is not reachable, or we are in a region
461 // which doesn't respect dominance.
462 if (verifyRecursively && op.getNumRegions() != 0) {
463 // If this operation is IsolatedFromAbove, then we'll handle it in
464 // the outer verification loop.
465 if (op.hasTrait<OpTrait::IsIsolatedFromAbove>())
466 continue;
467 worklist.push_back(&op);
468 }
469 }
470 }
471 }
472
473 return success();
474}
475
476//===----------------------------------------------------------------------===//
477// Entrypoint
478//===----------------------------------------------------------------------===//
479
480LogicalResult mlir::verify(Operation *op, bool verifyRecursively) {
481 OperationVerifier verifier(verifyRecursively);
482 return verifier.verifyOpAndDominance(*op);
483}
return success()
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.
Definition PDL.cpp:62
static std::string diag(const llvm::Value &value)
static bool mayBeValidWithoutTerminator(Block *block)
Returns true if this block may be valid without terminator.
Definition Verifier.cpp:103
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...
Definition Verifier.cpp:382
Block represents an ordered list of Operations.
Definition Block.h:33
bool empty()
Definition Block.h:158
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
Definition Block.cpp:27
SuccessorRange getSuccessors()
Definition Block.h:280
Operation & back()
Definition Block.h:162
BlockArgListType getArguments()
Definition Block.h:97
bool hasNoPredecessors()
Return true if this block has no predecessors.
Definition Block.h:255
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
Definition Block.cpp:31
unsigned computeBlockNumber()
Compute the position of this block within its parent region using an O(N) linear scan.
Definition Block.cpp:144
This class contains all of the information necessary to report a diagnostic to the DiagnosticEngine.
StringRef getNamespace() const
Definition Dialect.h:54
bool allowsUnknownOperations() const
Returns true if this dialect allows for unregistered operations, i.e.
Definition Dialect.h:62
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...
Definition Location.h:76
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.
Definition Operation.h:88
Value getOperand(unsigned idx)
Definition Operation.h:379
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
Definition Operation.h:778
unsigned getNumSuccessors()
Definition Operation.h:735
bool isRegistered()
Returns true if this operation has a registered operation description, otherwise false.
Definition Operation.h:129
bool mightHaveTrait()
Returns true if the operation might have the provided trait.
Definition Operation.h:786
Block * getBlock()
Returns the operation block that contains this operation.
Definition Operation.h:234
unsigned getNumRegions()
Returns the number of regions held by this operation.
Definition Operation.h:703
Location getLoc()
The source location the operation was defined or derived from.
Definition Operation.h:244
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.
Definition Operation.h:119
DictionaryAttr getDiscardableAttrDictionary()
Return all of the discardable attributes on this operation as a DictionaryAttr.
Definition Operation.h:530
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
Definition Operation.h:706
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition Operation.h:407
result_range getResults()
Definition Operation.h:444
MLIRContext * getContext()
Return the context this operation is associated with.
Definition Operation.h:237
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.
Definition Region.h:26
Block & front()
Definition Region.h:65
bool empty()
Definition Region.h:60
bool isProperAncestor(Region *other)
Return true if this region is a proper ancestor of the other region.
Definition Region.cpp:50
Location getLoc()
Return a location for this region.
Definition Region.cpp:31
bool hasOneBlock()
Return true if this region has exactly one block.
Definition Region.h:68
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition Value.cpp:18
bool isReachableFromEntry(Block *a) const
Return true if the specified block is reachable from the entry block of its region.
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:717
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.
Definition Threading.h:117
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,...
Definition Verifier.cpp:480