MLIR  17.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/StringMap.h"
35 #include "llvm/Support/FormatVariadic.h"
36 #include "llvm/Support/PrettyStackTrace.h"
37 #include "llvm/Support/Regex.h"
38 #include <atomic>
39 #include <optional>
40 
41 using namespace mlir;
42 
43 namespace {
44 /// This class encapsulates all the state used to verify an operation region.
45 class OperationVerifier {
46 public:
47  /// If `verifyRecursively` is true, then this will also recursively verify
48  /// nested operations.
49  explicit OperationVerifier(bool verifyRecursively)
50  : verifyRecursively(verifyRecursively) {}
51 
52  /// Verify the given operation.
53  LogicalResult verifyOpAndDominance(Operation &op);
54 
55 private:
56  /// Any ops that have regions and are marked as "isolated from above" will be
57  /// returned in the opsWithIsolatedRegions vector.
59  verifyBlock(Block &block,
60  SmallVectorImpl<Operation *> &opsWithIsolatedRegions);
61 
62  /// Verify the properties and dominance relationships of this operation.
63  LogicalResult verifyOperation(Operation &op);
64 
65  /// Verify the dominance property of regions contained within the given
66  /// Operation.
67  LogicalResult verifyDominanceOfContainedRegions(Operation &op,
68  DominanceInfo &domInfo);
69 
70  /// A flag indicating if this verifier should recursively verify nested
71  /// operations.
72  bool verifyRecursively;
73 };
74 } // namespace
75 
76 LogicalResult OperationVerifier::verifyOpAndDominance(Operation &op) {
77  // Verify the operation first, collecting any IsolatedFromAbove operations.
78  if (failed(verifyOperation(op)))
79  return failure();
80 
81  // Since everything looks structurally ok to this point, we do a dominance
82  // check for any nested regions. We do this as a second pass since malformed
83  // CFG's can cause dominator analysis construction to crash and we want the
84  // verifier to be resilient to malformed code.
85  if (op.getNumRegions() != 0) {
86  DominanceInfo domInfo;
87  if (failed(verifyDominanceOfContainedRegions(op, domInfo)))
88  return failure();
89  }
90 
91  return success();
92 }
93 
94 /// Returns true if this block may be valid without terminator. That is if:
95 /// - it does not have a parent region.
96 /// - Or the parent region have a single block and:
97 /// - This region does not have a parent op.
98 /// - Or the parent op is unregistered.
99 /// - Or the parent op has the NoTerminator trait.
100 static bool mayBeValidWithoutTerminator(Block *block) {
101  if (!block->getParent())
102  return true;
103  if (!llvm::hasSingleElement(*block->getParent()))
104  return false;
105  Operation *op = block->getParentOp();
106  return !op || op->mightHaveTrait<OpTrait::NoTerminator>();
107 }
108 
109 LogicalResult OperationVerifier::verifyBlock(
110  Block &block, SmallVectorImpl<Operation *> &opsWithIsolatedRegions) {
111 
112  for (auto arg : block.getArguments())
113  if (arg.getOwner() != &block)
114  return emitError(arg.getLoc(), "block argument not owned by block");
115 
116  // Verify that this block has a terminator.
117  if (block.empty()) {
118  if (mayBeValidWithoutTerminator(&block))
119  return success();
120  return emitError(block.getParent()->getLoc(),
121  "empty block: expect at least a terminator");
122  }
123 
124  // Check each operation, and make sure there are no branches out of the
125  // middle of this block.
126  for (Operation &op : block) {
127  // Only the last instructions is allowed to have successors.
128  if (op.getNumSuccessors() != 0 && &op != &block.back())
129  return op.emitError(
130  "operation with block successors must terminate its parent block");
131 
132  // If we aren't verifying recursievly, there is nothing left to check.
133  if (!verifyRecursively)
134  continue;
135 
136  // If this operation has regions and is IsolatedFromAbove, we defer
137  // checking. This allows us to parallelize verification better.
138  if (op.getNumRegions() != 0 &&
140  opsWithIsolatedRegions.push_back(&op);
141 
142  // Otherwise, check the operation inline.
143  } else if (failed(verifyOperation(op))) {
144  return failure();
145  }
146  }
147 
148  // Verify that this block is not branching to a block of a different
149  // region.
150  for (Block *successor : block.getSuccessors())
151  if (successor->getParent() != block.getParent())
152  return block.back().emitOpError(
153  "branching to block of a different region");
154 
155  // If this block doesn't have to have a terminator, don't require it.
156  if (mayBeValidWithoutTerminator(&block))
157  return success();
158 
159  Operation &terminator = block.back();
160  if (!terminator.mightHaveTrait<OpTrait::IsTerminator>())
161  return block.back().emitError("block with no terminator, has ")
162  << terminator;
163 
164  return success();
165 }
166 
167 /// Verify the properties and dominance relationships of this operation,
168 /// stopping region recursion at any "isolated from above operations". Any such
169 /// ops are returned in the opsWithIsolatedRegions vector.
170 LogicalResult OperationVerifier::verifyOperation(Operation &op) {
171  // Check that operands are non-nil and structurally ok.
172  for (auto operand : op.getOperands())
173  if (!operand)
174  return op.emitError("null operand found");
175 
176  /// Verify that all of the attributes are okay.
177  for (auto attr : op.getAttrs()) {
178  // Check for any optional dialect specific attributes.
179  if (auto *dialect = attr.getNameDialect())
180  if (failed(dialect->verifyOperationAttribute(&op, attr)))
181  return failure();
182  }
183 
184  // If we can get operation info for this, check the custom hook.
185  OperationName opName = op.getName();
186  std::optional<RegisteredOperationName> registeredInfo =
187  opName.getRegisteredInfo();
188  if (registeredInfo && failed(registeredInfo->verifyInvariants(&op)))
189  return failure();
190 
191  SmallVector<Operation *> opsWithIsolatedRegions;
192 
193  if (unsigned numRegions = op.getNumRegions()) {
194  auto kindInterface = dyn_cast<RegionKindInterface>(op);
195 
196  // Verify that all child regions are ok.
197  MutableArrayRef<Region> regions = op.getRegions();
198  for (unsigned i = 0; i < numRegions; ++i) {
199  Region &region = regions[i];
200  RegionKind kind =
201  kindInterface ? kindInterface.getRegionKind(i) : RegionKind::SSACFG;
202  // Check that Graph Regions only have a single basic block. This is
203  // similar to the code in SingleBlockImplicitTerminator, but doesn't
204  // require the trait to be specified. This arbitrary limitation is
205  // designed to limit the number of cases that have to be handled by
206  // transforms and conversions.
207  if (op.isRegistered() && kind == RegionKind::Graph) {
208  // Non-empty regions must contain a single basic block.
209  if (!region.empty() && !region.hasOneBlock())
210  return op.emitOpError("expects graph region #")
211  << i << " to have 0 or 1 blocks";
212  }
213 
214  if (region.empty())
215  continue;
216 
217  // Verify the first block has no predecessors.
218  Block *firstBB = &region.front();
219  if (!firstBB->hasNoPredecessors())
220  return emitError(op.getLoc(),
221  "entry block of region may not have predecessors");
222 
223  // Verify each of the blocks within the region if we are verifying
224  // recursively.
225  if (verifyRecursively) {
226  for (Block &block : region)
227  if (failed(verifyBlock(block, opsWithIsolatedRegions)))
228  return failure();
229  }
230  }
231  }
232 
233  // Verify the nested ops that are able to be verified in parallel.
235  op.getContext(), opsWithIsolatedRegions,
236  [&](Operation *op) { return verifyOpAndDominance(*op); })))
237  return failure();
238 
239  // After the region ops are verified, run the verifiers that have additional
240  // region invariants need to veirfy.
241  if (registeredInfo && failed(registeredInfo->verifyRegionInvariants(&op)))
242  return failure();
243 
244  // If this is a registered operation, there is nothing left to do.
245  if (registeredInfo)
246  return success();
247 
248  // Otherwise, verify that the parent dialect allows un-registered operations.
249  Dialect *dialect = opName.getDialect();
250  if (!dialect) {
251  if (!op.getContext()->allowsUnregisteredDialects()) {
252  return op.emitOpError()
253  << "created with unregistered dialect. If this is "
254  "intended, please call allowUnregisteredDialects() on the "
255  "MLIRContext, or use -allow-unregistered-dialect with "
256  "the MLIR opt tool used";
257  }
258  return success();
259  }
260 
261  if (!dialect->allowsUnknownOperations()) {
262  return op.emitError("unregistered operation '")
263  << op.getName() << "' found in dialect ('" << dialect->getNamespace()
264  << "') that does not allow unknown operations";
265  }
266 
267  return success();
268 }
269 
270 //===----------------------------------------------------------------------===//
271 // Dominance Checking
272 //===----------------------------------------------------------------------===//
273 
274 /// Emit an error when the specified operand of the specified operation is an
275 /// invalid use because of dominance properties.
276 static void diagnoseInvalidOperandDominance(Operation &op, unsigned operandNo) {
277  InFlightDiagnostic diag = op.emitError("operand #")
278  << operandNo << " does not dominate this use";
279 
280  Value operand = op.getOperand(operandNo);
281 
282  /// Attach a note to an in-flight diagnostic that provide more information
283  /// about where an op operand is defined.
284  if (auto *useOp = operand.getDefiningOp()) {
285  Diagnostic &note = diag.attachNote(useOp->getLoc());
286  note << "operand defined here";
287  Block *block1 = op.getBlock();
288  Block *block2 = useOp->getBlock();
289  Region *region1 = block1->getParent();
290  Region *region2 = block2->getParent();
291  if (block1 == block2)
292  note << " (op in the same block)";
293  else if (region1 == region2)
294  note << " (op in the same region)";
295  else if (region2->isProperAncestor(region1))
296  note << " (op in a parent region)";
297  else if (region1->isProperAncestor(region2))
298  note << " (op in a child region)";
299  else
300  note << " (op is neither in a parent nor in a child region)";
301  return;
302  }
303  // Block argument case.
304  Block *block1 = op.getBlock();
305  Block *block2 = operand.cast<BlockArgument>().getOwner();
306  Region *region1 = block1->getParent();
307  Region *region2 = block2->getParent();
308  Location loc = UnknownLoc::get(op.getContext());
309  if (block2->getParentOp())
310  loc = block2->getParentOp()->getLoc();
311  Diagnostic &note = diag.attachNote(loc);
312  if (!region2) {
313  note << " (block without parent)";
314  return;
315  }
316  if (block1 == block2)
317  llvm::report_fatal_error("Internal error in dominance verification");
318  int index = std::distance(region2->begin(), block2->getIterator());
319  note << "operand defined as a block argument (block #" << index;
320  if (region1 == region2)
321  note << " in the same region)";
322  else if (region2->isProperAncestor(region1))
323  note << " in a parent region)";
324  else if (region1->isProperAncestor(region2))
325  note << " in a child region)";
326  else
327  note << " neither in a parent nor in a child region)";
328 }
329 
330 /// Verify the dominance of each of the nested blocks within the given operation
332 OperationVerifier::verifyDominanceOfContainedRegions(Operation &op,
333  DominanceInfo &domInfo) {
334  for (Region &region : op.getRegions()) {
335  // Verify the dominance of each of the held operations.
336  for (Block &block : region) {
337  // Dominance is only meaningful inside reachable blocks.
338  bool isReachable = domInfo.isReachableFromEntry(&block);
339 
340  for (Operation &op : block) {
341  if (isReachable) {
342  // Check that operands properly dominate this use.
343  for (const auto &operand : llvm::enumerate(op.getOperands())) {
344  if (domInfo.properlyDominates(operand.value(), &op))
345  continue;
346 
347  diagnoseInvalidOperandDominance(op, operand.index());
348  return failure();
349  }
350  }
351 
352  // Recursively verify dominance within each operation in the block, even
353  // if the block itself is not reachable, or we are in a region which
354  // doesn't respect dominance.
355  if (verifyRecursively && op.getNumRegions() != 0) {
356  // If this operation is IsolatedFromAbove, then we'll handle it in the
357  // outer verification loop.
359  continue;
360 
361  if (failed(verifyDominanceOfContainedRegions(op, domInfo)))
362  return failure();
363  }
364  }
365  }
366  }
367  return success();
368 }
369 
370 //===----------------------------------------------------------------------===//
371 // Entrypoint
372 //===----------------------------------------------------------------------===//
373 
374 LogicalResult mlir::verify(Operation *op, bool verifyRecursively) {
375  OperationVerifier verifier(verifyRecursively);
376  return verifier.verifyOpAndDominance(*op);
377 }
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:100
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:276
This class represents an argument of a Block.
Definition: Value.h:304
Block represents an ordered list of Operations.
Definition: Block.h:30
bool empty()
Definition: Block.h:137
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
Definition: Block.cpp:26
SuccessorRange getSuccessors()
Definition: Block.h:253
BlockArgListType getArguments()
Definition: Block.h:76
bool hasNoPredecessors()
Return true if this block has no predecessors.
Definition: Block.h:228
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
Definition: Block.cpp:30
This class contains all of the information necessary to report a diagnostic to the DiagnosticEngine.
Definition: Diagnostics.h:156
Dialects are groups of MLIR operations, types and attributes, as well as behavior associated with the...
Definition: Dialect.h:41
StringRef getNamespace() const
Definition: Dialect.h:57
bool allowsUnknownOperations() const
Returns true if this dialect allows for unregistered operations, i.e.
Definition: Dialect.h:65
A class for computing basic dominance information.
Definition: Dominance.h:121
bool properlyDominates(Operation *a, Operation *b, bool enclosingOpOk=true) const
Return true if operation A properly dominates operation B, i.e.
Definition: Dominance.h:134
This class represents a diagnostic that is inflight and set to be reported.
Definition: Diagnostics.h:308
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition: Location.h:63
bool allowsUnregisteredDialects()
Return true if we allow to create operation for unregistered dialects.
This class provides the API for ops that are known to be isolated from above.
This class provides the API for ops that are known to be terminators.
Definition: OpDefinition.h:703
This class indicates that the regions associated with this op don't have terminators.
Definition: OpDefinition.h:699
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.
Operation is the basic unit of execution within MLIR.
Definition: Operation.h:75
Value getOperand(unsigned idx)
Definition: Operation.h:329
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
Definition: Operation.h:592
unsigned getNumSuccessors()
Definition: Operation.h:570
bool isRegistered()
Returns true if this operation has a registered operation description, otherwise false.
Definition: Operation.h:113
bool mightHaveTrait()
Returns true if the operation might have the provided trait.
Definition: Operation.h:600
MLIRContext * getContext()
Return the context this operation is associated with.
Definition: Operation.h:200
unsigned getNumRegions()
Returns the number of regions held by this operation.
Definition: Operation.h:537
Location getLoc()
The source location the operation was defined or derived from.
Definition: Operation.h:207
ArrayRef< NamedAttribute > getAttrs()
Return all of the attributes on this operation.
Definition: Operation.h:418
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
Definition: Operation.cpp:234
Block * getBlock()
Returns the operation block that contains this operation.
Definition: Operation.h:197
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
Definition: Operation.h:540
OperationName getName()
The name of an operation is the key identifier for it.
Definition: Operation.h:103
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition: Operation.h:357
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
Definition: Operation.cpp:520
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition: Region.h:26
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
iterator begin()
Definition: Region.h:55
Location getLoc()
Return a location for this region.
Definition: Region.cpp:31
Block & front()
Definition: Region.h:65
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:93
U cast() const
Definition: Value.h:113
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition: Value.cpp:20
bool isReachableFromEntry(Block *a) const
Return true if the specified block is reachable from the entry block of its region.
Definition: Dominance.cpp:232
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
Definition: Matchers.h:223
This header declares functions that assit transformations in the MemRef dialect.
LogicalResult failure(bool isFailure=true)
Utility function to generate a LogicalResult.
Definition: LogicalResult.h:62
LogicalResult failableParallelForEach(MLIRContext *context, IteratorT begin, IteratorT end, FuncT &&func)
Invoke the given function on the elements between [begin, end) asynchronously.
Definition: Threading.h:36
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
LogicalResult success(bool isSuccess=true)
Utility function to generate a LogicalResult.
Definition: LogicalResult.h:56
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:374
bool failed(LogicalResult result)
Utility function that returns true if the provided LogicalResult corresponds to a failure value.
Definition: LogicalResult.h:72
This class represents an efficient way to signal success or failure.
Definition: LogicalResult.h:26