MLIR  21.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 
37 using namespace mlir;
38 
39 namespace {
40 /// This class encapsulates all the state used to verify an operation region.
41 class OperationVerifier {
42 public:
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 
51 private:
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 
79 LogicalResult 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.
103 static bool mayBeValidWithoutTerminator(Block *block) {
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 
112 LogicalResult OperationVerifier::verifyOnEntrance(Block &block) {
113  for (auto arg : block.getArguments())
114  if (arg.getOwner() != &block)
115  return emitError(arg.getLoc(), "block argument not owned by block");
116 
117  // Verify that this block has a terminator.
118  if (block.empty()) {
119  if (mayBeValidWithoutTerminator(&block))
120  return success();
121  return emitError(block.getParent()->getLoc(),
122  "empty block: expect at least a terminator");
123  }
124 
125  // Check each operation, and make sure there are no branches out of the
126  // middle of this block.
127  for (Operation &op : block) {
128  // Only the last instructions is allowed to have successors.
129  if (op.getNumSuccessors() != 0 && &op != &block.back())
130  return op.emitError(
131  "operation with block successors must terminate its parent block");
132  }
133 
134  return success();
135 }
136 
137 LogicalResult OperationVerifier::verifyOnExit(Block &block) {
138  // Verify that this block is not branching to a block of a different
139  // region.
140  for (Block *successor : block.getSuccessors())
141  if (successor->getParent() != block.getParent())
142  return block.back().emitOpError(
143  "branching to block of a different region");
144 
145  // If this block doesn't have to have a terminator, don't require it.
146  if (mayBeValidWithoutTerminator(&block))
147  return success();
148 
149  Operation &terminator = block.back();
150  if (!terminator.mightHaveTrait<OpTrait::IsTerminator>())
151  return block.back().emitError("block with no terminator, has ")
152  << terminator;
153 
154  return success();
155 }
156 
157 LogicalResult OperationVerifier::verifyOnEntrance(Operation &op) {
158  // Check that operands are non-nil and structurally ok.
159  for (auto operand : op.getOperands())
160  if (!operand)
161  return op.emitError("null operand found");
162 
163  /// Verify that all of the attributes are okay.
164  for (auto attr : op.getDiscardableAttrDictionary()) {
165  // Check for any optional dialect specific attributes.
166  if (auto *dialect = attr.getNameDialect())
167  if (failed(dialect->verifyOperationAttribute(&op, attr)))
168  return failure();
169  }
170 
171  // If we can get operation info for this, check the custom hook.
172  OperationName opName = op.getName();
173  std::optional<RegisteredOperationName> registeredInfo =
174  opName.getRegisteredInfo();
175  if (registeredInfo && failed(registeredInfo->verifyInvariants(&op)))
176  return failure();
177 
178  unsigned numRegions = op.getNumRegions();
179  if (!numRegions)
180  return success();
181  auto kindInterface = dyn_cast<RegionKindInterface>(&op);
182  SmallVector<Operation *> opsWithIsolatedRegions;
183  // Verify that all child regions are ok.
184  MutableArrayRef<Region> regions = op.getRegions();
185  for (unsigned i = 0; i < numRegions; ++i) {
186  Region &region = regions[i];
187  RegionKind kind =
188  kindInterface ? kindInterface.getRegionKind(i) : RegionKind::SSACFG;
189  // Check that Graph Regions only have a single basic block. This is
190  // similar to the code in SingleBlockImplicitTerminator, but doesn't
191  // require the trait to be specified. This arbitrary limitation is
192  // designed to limit the number of cases that have to be handled by
193  // transforms and conversions.
194  if (op.isRegistered() && kind == RegionKind::Graph) {
195  // Non-empty regions must contain a single basic block.
196  if (!region.empty() && !region.hasOneBlock())
197  return op.emitOpError("expects graph region #")
198  << i << " to have 0 or 1 blocks";
199  }
200 
201  if (region.empty())
202  continue;
203 
204  // Verify the first block has no predecessors.
205  Block *firstBB = &region.front();
206  if (!firstBB->hasNoPredecessors())
207  return emitError(op.getLoc(),
208  "entry block of region may not have predecessors");
209  }
210  return success();
211 }
212 
213 LogicalResult OperationVerifier::verifyOnExit(Operation &op) {
214  SmallVector<Operation *> opsWithIsolatedRegions;
215  if (verifyRecursively) {
216  for (Region &region : op.getRegions())
217  for (Block &block : region)
218  for (Operation &o : block)
219  if (o.getNumRegions() != 0 &&
220  o.hasTrait<OpTrait::IsIsolatedFromAbove>())
221  opsWithIsolatedRegions.push_back(&o);
222  }
223 
224  std::atomic<bool> opFailedVerify = false;
225  parallelForEach(op.getContext(), opsWithIsolatedRegions, [&](Operation *o) {
226  if (failed(verifyOpAndDominance(*o)))
227  opFailedVerify.store(true, std::memory_order_relaxed);
228  });
229  if (opFailedVerify.load(std::memory_order_relaxed))
230  return failure();
231 
232  OperationName opName = op.getName();
233  std::optional<RegisteredOperationName> registeredInfo =
234  opName.getRegisteredInfo();
235  // After the region ops are verified, run the verifiers that have additional
236  // region invariants need to veirfy.
237  if (registeredInfo && failed(registeredInfo->verifyRegionInvariants(&op)))
238  return failure();
239 
240  // If this is a registered operation, there is nothing left to do.
241  if (registeredInfo)
242  return success();
243 
244  // Otherwise, verify that the parent dialect allows un-registered operations.
245  Dialect *dialect = opName.getDialect();
246  if (!dialect) {
247  if (!op.getContext()->allowsUnregisteredDialects()) {
248  return op.emitOpError()
249  << "created with unregistered dialect. If this is "
250  "intended, please call allowUnregisteredDialects() on the "
251  "MLIRContext, or use -allow-unregistered-dialect with "
252  "the MLIR opt tool used";
253  }
254  return success();
255  }
256 
257  if (!dialect->allowsUnknownOperations()) {
258  return op.emitError("unregistered operation '")
259  << op.getName() << "' found in dialect ('" << dialect->getNamespace()
260  << "') that does not allow unknown operations";
261  }
262 
263  return success();
264 }
265 
266 /// Verify the properties and dominance relationships of this operation,
267 /// stopping region "recursion" at any "isolated from above operations".
268 /// Such ops are collected separately and verified inside
269 /// verifyBlockPostChildren.
270 LogicalResult OperationVerifier::verifyOperation(Operation &op) {
271  SmallVector<WorkItemEntry> worklist{{&op, false}};
272  while (!worklist.empty()) {
273  WorkItemEntry &top = worklist.back();
274 
275  auto visit = [](auto &&visitor, WorkItem w) {
276  if (auto *o = dyn_cast<Operation *>(w))
277  return visitor(o);
278  return visitor(cast<Block *>(w));
279  };
280 
281  const bool isExit = top.getInt();
282  top.setInt(true);
283  auto item = top.getPointer();
284 
285  // 2nd visit of this work item ("exit").
286  if (isExit) {
287  if (failed(
288  visit([this](auto *workItem) { return verifyOnExit(*workItem); },
289  item)))
290  return failure();
291  worklist.pop_back();
292  continue;
293  }
294 
295  // 1st visit of this work item ("entrance").
296  if (failed(visit(
297  [this](auto *workItem) { return verifyOnEntrance(*workItem); },
298  item)))
299  return failure();
300 
301  if (Block *currentBlock = dyn_cast<Block *>(item)) {
302  // Skip "isolated from above operations".
303  for (Operation &o : llvm::reverse(*currentBlock)) {
304  if (o.getNumRegions() == 0 ||
305  !o.hasTrait<OpTrait::IsIsolatedFromAbove>())
306  worklist.emplace_back(&o);
307  }
308  continue;
309  }
310 
311  Operation &currentOp = *cast<Operation *>(item);
312  if (verifyRecursively)
313  for (Region &region : llvm::reverse(currentOp.getRegions()))
314  for (Block &block : llvm::reverse(region))
315  worklist.emplace_back(&block);
316  }
317  return success();
318 }
319 
320 //===----------------------------------------------------------------------===//
321 // Dominance Checking
322 //===----------------------------------------------------------------------===//
323 
324 /// Emit an error when the specified operand of the specified operation is an
325 /// invalid use because of dominance properties.
326 static void diagnoseInvalidOperandDominance(Operation &op, unsigned operandNo) {
327  InFlightDiagnostic diag = op.emitError("operand #")
328  << operandNo << " does not dominate this use";
329 
330  Value operand = op.getOperand(operandNo);
331 
332  /// Attach a note to an in-flight diagnostic that provide more information
333  /// about where an op operand is defined.
334  if (auto *useOp = operand.getDefiningOp()) {
335  Diagnostic &note = diag.attachNote(useOp->getLoc());
336  note << "operand defined here";
337  Block *block1 = op.getBlock();
338  Block *block2 = useOp->getBlock();
339  Region *region1 = block1->getParent();
340  Region *region2 = block2->getParent();
341  if (block1 == block2)
342  note << " (op in the same block)";
343  else if (region1 == region2)
344  note << " (op in the same region)";
345  else if (region2->isProperAncestor(region1))
346  note << " (op in a parent region)";
347  else if (region1->isProperAncestor(region2))
348  note << " (op in a child region)";
349  else
350  note << " (op is neither in a parent nor in a child region)";
351  return;
352  }
353  // Block argument case.
354  Block *block1 = op.getBlock();
355  Block *block2 = llvm::cast<BlockArgument>(operand).getOwner();
356  Region *region1 = block1->getParent();
357  Region *region2 = block2->getParent();
358  Location loc = UnknownLoc::get(op.getContext());
359  if (block2->getParentOp())
360  loc = block2->getParentOp()->getLoc();
361  Diagnostic &note = diag.attachNote(loc);
362  if (!region2) {
363  note << " (block without parent)";
364  return;
365  }
366  if (block1 == block2)
367  llvm::report_fatal_error("Internal error in dominance verification");
368  int index = std::distance(region2->begin(), block2->getIterator());
369  note << "operand defined as a block argument (block #" << index;
370  if (region1 == region2)
371  note << " in the same region)";
372  else if (region2->isProperAncestor(region1))
373  note << " in a parent region)";
374  else if (region1->isProperAncestor(region2))
375  note << " in a child region)";
376  else
377  note << " neither in a parent nor in a child region)";
378 }
379 
380 /// Verify the dominance of each of the nested blocks within the given operation
381 LogicalResult
382 OperationVerifier::verifyDominanceOfContainedRegions(Operation &op,
383  DominanceInfo &domInfo) {
384  llvm::SmallVector<Operation *, 8> worklist{&op};
385  while (!worklist.empty()) {
386  auto *op = worklist.pop_back_val();
387  for (auto &region : op->getRegions())
388  for (auto &block : region.getBlocks()) {
389  // Dominance is only meaningful inside reachable blocks.
390  bool isReachable = domInfo.isReachableFromEntry(&block);
391  for (auto &op : block) {
392  if (isReachable) {
393  // Check that operands properly dominate this use.
394  for (const auto &operand : llvm::enumerate(op.getOperands())) {
395  if (domInfo.properlyDominates(operand.value(), &op))
396  continue;
397 
398  diagnoseInvalidOperandDominance(op, operand.index());
399  return failure();
400  }
401  }
402 
403  // Recursively verify dominance within each operation in the block,
404  // even if the block itself is not reachable, or we are in a region
405  // which doesn't respect dominance.
406  if (verifyRecursively && op.getNumRegions() != 0) {
407  // If this operation is IsolatedFromAbove, then we'll handle it in
408  // the outer verification loop.
410  continue;
411  worklist.push_back(&op);
412  }
413  }
414  }
415  }
416 
417  return success();
418 }
419 
420 //===----------------------------------------------------------------------===//
421 // Entrypoint
422 //===----------------------------------------------------------------------===//
423 
424 LogicalResult mlir::verify(Operation *op, bool verifyRecursively) {
425  OperationVerifier verifier(verifyRecursively);
426  return verifier.verifyOpAndDominance(*op);
427 }
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:63
union mlir::linalg::@1191::ArityGroupAndKind::Kind kind
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:326
Block represents an ordered list of Operations.
Definition: Block.h:33
bool empty()
Definition: Block.h:148
Operation & back()
Definition: Block.h:152
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
Definition: Block.cpp:29
SuccessorRange getSuccessors()
Definition: Block.h:267
BlockArgListType getArguments()
Definition: Block.h:87
bool hasNoPredecessors()
Return true if this block has no predecessors.
Definition: Block.h:242
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
Definition: Block.cpp:33
This class contains all of the information necessary to report a diagnostic to the DiagnosticEngine.
Definition: Diagnostics.h:155
Dialects are groups of MLIR operations, types and attributes, as well as behavior associated with the...
Definition: Dialect.h:38
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
A class for computing basic dominance information.
Definition: Dominance.h:140
bool properlyDominates(Operation *a, Operation *b, bool enclosingOpOk=true) const
Return true if operation A properly dominates operation B, i.e.
Definition: Dominance.cpp:324
This class represents a diagnostic that is inflight and set to be reported.
Definition: Diagnostics.h:314
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition: Location.h:66
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:772
This class indicates that the regions associated with this op don't have terminators.
Definition: OpDefinition.h:768
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:88
Value getOperand(unsigned idx)
Definition: Operation.h:350
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
Definition: Operation.h:750
unsigned getNumSuccessors()
Definition: Operation.h:707
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:758
MLIRContext * getContext()
Return the context this operation is associated with.
Definition: Operation.h:216
unsigned getNumRegions()
Returns the number of regions held by this operation.
Definition: Operation.h:674
Location getLoc()
The source location the operation was defined or derived from.
Definition: Operation.h:223
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
Definition: Operation.cpp:268
Block * getBlock()
Returns the operation block that contains this operation.
Definition: Operation.h:213
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
Definition: Operation.h:677
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:501
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition: Operation.h:378
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
Definition: Operation.cpp:673
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:96
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:307
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
Definition: Matchers.h:344
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
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,...
Definition: Verifier.cpp:424