MLIR  20.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/DenseMapInfoVariant.h"
35 #include "llvm/ADT/PointerIntPair.h"
36 #include "llvm/ADT/StringMap.h"
37 #include "llvm/Support/FormatVariadic.h"
38 #include "llvm/Support/PrettyStackTrace.h"
39 #include "llvm/Support/Regex.h"
40 #include <atomic>
41 #include <optional>
42 
43 using namespace mlir;
44 
45 namespace {
46 /// This class encapsulates all the state used to verify an operation region.
47 class OperationVerifier {
48 public:
49  /// If `verifyRecursively` is true, then this will also recursively verify
50  /// nested operations.
51  explicit OperationVerifier(bool verifyRecursively)
52  : verifyRecursively(verifyRecursively) {}
53 
54  /// Verify the given operation.
55  LogicalResult verifyOpAndDominance(Operation &op);
56 
57 private:
59  using WorkItemEntry = llvm::PointerIntPair<WorkItem, 1, bool>;
60 
61  /// This verifier uses a DFS of the tree of operations/blocks. The method
62  /// verifyOnEntrance is invoked when we visit a node for the first time, i.e.
63  /// before visiting its children. The method verifyOnExit is invoked
64  /// upon exit from the subtree, i.e. when we visit a node for the second time.
65  LogicalResult verifyOnEntrance(Block &block);
66  LogicalResult verifyOnEntrance(Operation &op);
67 
68  LogicalResult verifyOnExit(Block &block);
69  LogicalResult verifyOnExit(Operation &op);
70 
71  /// Verify the properties and dominance relationships of this operation.
72  LogicalResult verifyOperation(Operation &op);
73 
74  /// Verify the dominance property of regions contained within the given
75  /// Operation.
76  LogicalResult verifyDominanceOfContainedRegions(Operation &op,
77  DominanceInfo &domInfo);
78 
79  /// A flag indicating if this verifier should recursively verify nested
80  /// operations.
81  bool verifyRecursively;
82 };
83 } // namespace
84 
85 LogicalResult OperationVerifier::verifyOpAndDominance(Operation &op) {
86  // Verify the operation first, collecting any IsolatedFromAbove operations.
87  if (failed(verifyOperation(op)))
88  return failure();
89 
90  // Since everything looks structurally ok to this point, we do a dominance
91  // check for any nested regions. We do this as a second pass since malformed
92  // CFG's can cause dominator analysis construction to crash and we want the
93  // verifier to be resilient to malformed code.
94  if (op.getNumRegions() != 0) {
95  DominanceInfo domInfo;
96  if (failed(verifyDominanceOfContainedRegions(op, domInfo)))
97  return failure();
98  }
99 
100  return success();
101 }
102 
103 /// Returns true if this block may be valid without terminator. That is if:
104 /// - it does not have a parent region.
105 /// - Or the parent region have a single block and:
106 /// - This region does not have a parent op.
107 /// - Or the parent op is unregistered.
108 /// - Or the parent op has the NoTerminator trait.
109 static bool mayBeValidWithoutTerminator(Block *block) {
110  if (!block->getParent())
111  return true;
112  if (!llvm::hasSingleElement(*block->getParent()))
113  return false;
114  Operation *op = block->getParentOp();
115  return !op || op->mightHaveTrait<OpTrait::NoTerminator>();
116 }
117 
118 LogicalResult OperationVerifier::verifyOnEntrance(Block &block) {
119  for (auto arg : block.getArguments())
120  if (arg.getOwner() != &block)
121  return emitError(arg.getLoc(), "block argument not owned by block");
122 
123  // Verify that this block has a terminator.
124  if (block.empty()) {
125  if (mayBeValidWithoutTerminator(&block))
126  return success();
127  return emitError(block.getParent()->getLoc(),
128  "empty block: expect at least a terminator");
129  }
130 
131  // Check each operation, and make sure there are no branches out of the
132  // middle of this block.
133  for (Operation &op : block) {
134  // Only the last instructions is allowed to have successors.
135  if (op.getNumSuccessors() != 0 && &op != &block.back())
136  return op.emitError(
137  "operation with block successors must terminate its parent block");
138  }
139 
140  return success();
141 }
142 
143 LogicalResult OperationVerifier::verifyOnExit(Block &block) {
144  // Verify that this block is not branching to a block of a different
145  // region.
146  for (Block *successor : block.getSuccessors())
147  if (successor->getParent() != block.getParent())
148  return block.back().emitOpError(
149  "branching to block of a different region");
150 
151  // If this block doesn't have to have a terminator, don't require it.
152  if (mayBeValidWithoutTerminator(&block))
153  return success();
154 
155  Operation &terminator = block.back();
156  if (!terminator.mightHaveTrait<OpTrait::IsTerminator>())
157  return block.back().emitError("block with no terminator, has ")
158  << terminator;
159 
160  return success();
161 }
162 
163 LogicalResult OperationVerifier::verifyOnEntrance(Operation &op) {
164  // Check that operands are non-nil and structurally ok.
165  for (auto operand : op.getOperands())
166  if (!operand)
167  return op.emitError("null operand found");
168 
169  /// Verify that all of the attributes are okay.
170  for (auto attr : op.getDiscardableAttrDictionary()) {
171  // Check for any optional dialect specific attributes.
172  if (auto *dialect = attr.getNameDialect())
173  if (failed(dialect->verifyOperationAttribute(&op, attr)))
174  return failure();
175  }
176 
177  // If we can get operation info for this, check the custom hook.
178  OperationName opName = op.getName();
179  std::optional<RegisteredOperationName> registeredInfo =
180  opName.getRegisteredInfo();
181  if (registeredInfo && failed(registeredInfo->verifyInvariants(&op)))
182  return failure();
183 
184  unsigned numRegions = op.getNumRegions();
185  if (!numRegions)
186  return success();
187  auto kindInterface = dyn_cast<RegionKindInterface>(&op);
188  SmallVector<Operation *> opsWithIsolatedRegions;
189  // Verify that all child regions are ok.
190  MutableArrayRef<Region> regions = op.getRegions();
191  for (unsigned i = 0; i < numRegions; ++i) {
192  Region &region = regions[i];
193  RegionKind kind =
194  kindInterface ? kindInterface.getRegionKind(i) : RegionKind::SSACFG;
195  // Check that Graph Regions only have a single basic block. This is
196  // similar to the code in SingleBlockImplicitTerminator, but doesn't
197  // require the trait to be specified. This arbitrary limitation is
198  // designed to limit the number of cases that have to be handled by
199  // transforms and conversions.
200  if (op.isRegistered() && kind == RegionKind::Graph) {
201  // Non-empty regions must contain a single basic block.
202  if (!region.empty() && !region.hasOneBlock())
203  return op.emitOpError("expects graph region #")
204  << i << " to have 0 or 1 blocks";
205  }
206 
207  if (region.empty())
208  continue;
209 
210  // Verify the first block has no predecessors.
211  Block *firstBB = &region.front();
212  if (!firstBB->hasNoPredecessors())
213  return emitError(op.getLoc(),
214  "entry block of region may not have predecessors");
215  }
216  return success();
217 }
218 
219 LogicalResult OperationVerifier::verifyOnExit(Operation &op) {
220  SmallVector<Operation *> opsWithIsolatedRegions;
221  if (verifyRecursively) {
222  for (Region &region : op.getRegions())
223  for (Block &block : region)
224  for (Operation &o : block)
225  if (o.getNumRegions() != 0 &&
226  o.hasTrait<OpTrait::IsIsolatedFromAbove>())
227  opsWithIsolatedRegions.push_back(&o);
228  }
229  if (failed(failableParallelForEach(
230  op.getContext(), opsWithIsolatedRegions,
231  [&](Operation *o) { return verifyOpAndDominance(*o); })))
232  return failure();
233  OperationName opName = op.getName();
234  std::optional<RegisteredOperationName> registeredInfo =
235  opName.getRegisteredInfo();
236  // After the region ops are verified, run the verifiers that have additional
237  // region invariants need to veirfy.
238  if (registeredInfo && failed(registeredInfo->verifyRegionInvariants(&op)))
239  return failure();
240 
241  // If this is a registered operation, there is nothing left to do.
242  if (registeredInfo)
243  return success();
244 
245  // Otherwise, verify that the parent dialect allows un-registered operations.
246  Dialect *dialect = opName.getDialect();
247  if (!dialect) {
248  if (!op.getContext()->allowsUnregisteredDialects()) {
249  return op.emitOpError()
250  << "created with unregistered dialect. If this is "
251  "intended, please call allowUnregisteredDialects() on the "
252  "MLIRContext, or use -allow-unregistered-dialect with "
253  "the MLIR opt tool used";
254  }
255  return success();
256  }
257 
258  if (!dialect->allowsUnknownOperations()) {
259  return op.emitError("unregistered operation '")
260  << op.getName() << "' found in dialect ('" << dialect->getNamespace()
261  << "') that does not allow unknown operations";
262  }
263 
264  return success();
265 }
266 
267 /// Verify the properties and dominance relationships of this operation,
268 /// stopping region "recursion" at any "isolated from above operations".
269 /// Such ops are collected separately and verified inside
270 /// verifyBlockPostChildren.
271 LogicalResult OperationVerifier::verifyOperation(Operation &op) {
272  SmallVector<WorkItemEntry> worklist{{&op, false}};
273  while (!worklist.empty()) {
274  WorkItemEntry &top = worklist.back();
275 
276  auto visit = [](auto &&visitor, WorkItem w) {
277  if (w.is<Operation *>())
278  return visitor(w.get<Operation *>());
279  return visitor(w.get<Block *>());
280  };
281 
282  const bool isExit = top.getInt();
283  top.setInt(true);
284  auto item = top.getPointer();
285 
286  // 2nd visit of this work item ("exit").
287  if (isExit) {
288  if (failed(
289  visit([this](auto *workItem) { return verifyOnExit(*workItem); },
290  item)))
291  return failure();
292  worklist.pop_back();
293  continue;
294  }
295 
296  // 1st visit of this work item ("entrance").
297  if (failed(visit(
298  [this](auto *workItem) { return verifyOnEntrance(*workItem); },
299  item)))
300  return failure();
301 
302  if (item.is<Block *>()) {
303  Block &currentBlock = *item.get<Block *>();
304  // Skip "isolated from above operations".
305  for (Operation &o : llvm::reverse(currentBlock)) {
306  if (o.getNumRegions() == 0 ||
308  worklist.emplace_back(&o);
309  }
310  continue;
311  }
312 
313  Operation &currentOp = *item.get<Operation *>();
314  if (verifyRecursively)
315  for (Region &region : llvm::reverse(currentOp.getRegions()))
316  for (Block &block : llvm::reverse(region))
317  worklist.emplace_back(&block);
318  }
319  return success();
320 }
321 
322 //===----------------------------------------------------------------------===//
323 // Dominance Checking
324 //===----------------------------------------------------------------------===//
325 
326 /// Emit an error when the specified operand of the specified operation is an
327 /// invalid use because of dominance properties.
328 static void diagnoseInvalidOperandDominance(Operation &op, unsigned operandNo) {
329  InFlightDiagnostic diag = op.emitError("operand #")
330  << operandNo << " does not dominate this use";
331 
332  Value operand = op.getOperand(operandNo);
333 
334  /// Attach a note to an in-flight diagnostic that provide more information
335  /// about where an op operand is defined.
336  if (auto *useOp = operand.getDefiningOp()) {
337  Diagnostic &note = diag.attachNote(useOp->getLoc());
338  note << "operand defined here";
339  Block *block1 = op.getBlock();
340  Block *block2 = useOp->getBlock();
341  Region *region1 = block1->getParent();
342  Region *region2 = block2->getParent();
343  if (block1 == block2)
344  note << " (op in the same block)";
345  else if (region1 == region2)
346  note << " (op in the same region)";
347  else if (region2->isProperAncestor(region1))
348  note << " (op in a parent region)";
349  else if (region1->isProperAncestor(region2))
350  note << " (op in a child region)";
351  else
352  note << " (op is neither in a parent nor in a child region)";
353  return;
354  }
355  // Block argument case.
356  Block *block1 = op.getBlock();
357  Block *block2 = llvm::cast<BlockArgument>(operand).getOwner();
358  Region *region1 = block1->getParent();
359  Region *region2 = block2->getParent();
360  Location loc = UnknownLoc::get(op.getContext());
361  if (block2->getParentOp())
362  loc = block2->getParentOp()->getLoc();
363  Diagnostic &note = diag.attachNote(loc);
364  if (!region2) {
365  note << " (block without parent)";
366  return;
367  }
368  if (block1 == block2)
369  llvm::report_fatal_error("Internal error in dominance verification");
370  int index = std::distance(region2->begin(), block2->getIterator());
371  note << "operand defined as a block argument (block #" << index;
372  if (region1 == region2)
373  note << " in the same region)";
374  else if (region2->isProperAncestor(region1))
375  note << " in a parent region)";
376  else if (region1->isProperAncestor(region2))
377  note << " in a child region)";
378  else
379  note << " neither in a parent nor in a child region)";
380 }
381 
382 /// Verify the dominance of each of the nested blocks within the given operation
383 LogicalResult
384 OperationVerifier::verifyDominanceOfContainedRegions(Operation &op,
385  DominanceInfo &domInfo) {
386  llvm::SmallVector<Operation *, 8> worklist{&op};
387  while (!worklist.empty()) {
388  auto *op = worklist.pop_back_val();
389  for (auto &region : op->getRegions())
390  for (auto &block : region.getBlocks()) {
391  // Dominance is only meaningful inside reachable blocks.
392  bool isReachable = domInfo.isReachableFromEntry(&block);
393  for (auto &op : block) {
394  if (isReachable) {
395  // Check that operands properly dominate this use.
396  for (const auto &operand : llvm::enumerate(op.getOperands())) {
397  if (domInfo.properlyDominates(operand.value(), &op))
398  continue;
399 
400  diagnoseInvalidOperandDominance(op, operand.index());
401  return failure();
402  }
403  }
404 
405  // Recursively verify dominance within each operation in the block,
406  // even if the block itself is not reachable, or we are in a region
407  // which doesn't respect dominance.
408  if (verifyRecursively && op.getNumRegions() != 0) {
409  // If this operation is IsolatedFromAbove, then we'll handle it in
410  // the outer verification loop.
412  continue;
413  worklist.push_back(&op);
414  }
415  }
416  }
417  }
418 
419  return success();
420 }
421 
422 //===----------------------------------------------------------------------===//
423 // Entrypoint
424 //===----------------------------------------------------------------------===//
425 
426 LogicalResult mlir::verify(Operation *op, bool verifyRecursively) {
427  OperationVerifier verifier(verifyRecursively);
428  return verifier.verifyOpAndDominance(*op);
429 }
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
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:109
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:328
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.h:153
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:764
This class indicates that the regions associated with this op don't have terminators.
Definition: OpDefinition.h:760
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:345
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
Definition: Operation.h:745
unsigned getNumSuccessors()
Definition: Operation.h:702
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:753
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:669
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:672
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:496
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition: Operation.h:373
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
Definition: Operation.cpp:671
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:295
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
Definition: Matchers.h:344
Include the generated interface declarations.
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.
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:426