MLIR: lib/IR/Verifier.cpp Source File (original) (raw)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

34 #include "llvm/ADT/PointerIntPair.h"

35 #include

36

37 using namespace mlir;

38

39 namespace {

40

41 class OperationVerifier {

42 public:

43

44

45 explicit OperationVerifier(bool verifyRecursively)

46 : verifyRecursively(verifyRecursively) {}

47

48

49 LogicalResult verifyOpAndDominance(Operation &op);

50

51 private:

53 using WorkItemEntry = llvm::PointerIntPair<WorkItem, 1, bool>;

54

55

56

57

58

59 LogicalResult verifyOnEntrance(Block &block);

60 LogicalResult verifyOnEntrance(Operation &op);

61

62 LogicalResult verifyOnExit(Block &block);

63 LogicalResult verifyOnExit(Operation &op);

64

65

66 LogicalResult verifyOperation(Operation &op);

67

68

69

70 LogicalResult verifyDominanceOfContainedRegions(Operation &op,

72

73

74

75 bool verifyRecursively;

76 };

77 }

78

79 LogicalResult OperationVerifier::verifyOpAndDominance(Operation &op) {

80

81 if (failed(verifyOperation(op)))

82 return failure();

83

84

85

86

87

90 if (failed(verifyDominanceOfContainedRegions(op, domInfo)))

91 return failure();

92 }

93

94 return success();

95 }

96

97

98

99

100

101

102

105 return true;

106 if (!llvm::hasSingleElement(*block->getParent()))

107 return false;

110 }

111

112 LogicalResult OperationVerifier::verifyOnEntrance(Block &block) {

114 if (arg.getOwner() != &block)

115 return emitError(arg.getLoc(), "block argument not owned by block");

116

117

118 if (block.empty()) {

120 return success();

122 "empty block: expect at least a terminator");

123 }

124

125

126

128

131 "operation with block successors must terminate its parent block");

132 }

133

134 return success();

135 }

136

137 LogicalResult OperationVerifier::verifyOnExit(Block &block) {

138

139

141 if (successor->getParent() != block.getParent())

143 "branching to block of a different region");

144

145

147 return success();

148

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

160 if (!operand)

161 return op.emitError("null operand found");

162

163

165

166 if (auto *dialect = attr.getNameDialect())

167 if (failed(dialect->verifyOperationAttribute(&op, attr)))

168 return failure();

169 }

170

171

173 std::optional registeredInfo =

175 if (registeredInfo && failed(registeredInfo->verifyInvariants(&op)))

176 return failure();

177

179 if (!numRegions)

180 return success();

181 auto kindInterface = dyn_cast(&op);

182

184 for (unsigned i = 0; i < numRegions; ++i) {

185 Region &region = regions[i];

188

189

190

191

192

194

196 return op.emitOpError("expects graph region #")

197 << i << " to have 0 or 1 blocks";

198 }

199

200 if (region.empty())

201 continue;

202

203

207 "entry block of region may not have predecessors");

208 }

209 return success();

210 }

211

212 LogicalResult OperationVerifier::verifyOnExit(Operation &op) {

214 if (verifyRecursively) {

216 for (Block &block : region)

218 if (o.getNumRegions() != 0 &&

220 opsWithIsolatedRegions.push_back(&o);

221 }

222

223 std::atomic opFailedVerify = false;

225 if (failed(verifyOpAndDominance(*o)))

226 opFailedVerify.store(true, std::memory_order_relaxed);

227 });

228 if (opFailedVerify.load(std::memory_order_relaxed))

229 return failure();

230

232 std::optional registeredInfo =

234

235

236 if (registeredInfo && failed(registeredInfo->verifyRegionInvariants(&op)))

237 return failure();

238

239

240 if (registeredInfo)

241 return success();

242

243

245 if (!dialect) {

248 << "created with unregistered dialect. If this is "

249 "intended, please call allowUnregisteredDialects() on the "

250 "MLIRContext, or use -allow-unregistered-dialect with "

251 "the MLIR opt tool used";

252 }

253 return success();

254 }

255

257 return op.emitError("unregistered operation '")

259 << "') that does not allow unknown operations";

260 }

261

262 return success();

263 }

264

265

266

267

268

269 LogicalResult OperationVerifier::verifyOperation(Operation &op) {

271 while (!worklist.empty()) {

272 WorkItemEntry &top = worklist.back();

273

274 auto visit = [](auto &&visitor, WorkItem w) {

275 if (auto *o = dyn_cast<Operation *>(w))

276 return visitor(o);

277 return visitor(cast<Block *>(w));

278 };

279

280 const bool isExit = top.getInt();

281 top.setInt(true);

282 auto item = top.getPointer();

283

284

285 if (isExit) {

286 if (failed(

287 visit([this](auto *workItem) { return verifyOnExit(*workItem); },

288 item)))

289 return failure();

290 worklist.pop_back();

291 continue;

292 }

293

294

295 if (failed(visit(

296 [this](auto *workItem) { return verifyOnEntrance(*workItem); },

297 item)))

298 return failure();

299

300 if (Block *currentBlock = dyn_cast<Block *>(item)) {

301

302 for (Operation &o : llvm::reverse(*currentBlock)) {

303 if (o.getNumRegions() == 0 ||

305 worklist.emplace_back(&o);

306 }

307 continue;

308 }

309

310 Operation &currentOp = *cast<Operation *>(item);

311 if (verifyRecursively)

313 for (Block &block : llvm::reverse(region))

314 worklist.emplace_back(&block);

315 }

316 return success();

317 }

318

319

320

321

322

323

324

327 << operandNo << " does not dominate this use";

328

330

331

332

335 note << "operand defined here";

337 Block *block2 = useOp->getBlock();

340 if (block1 == block2)

341 note << " (op in the same block)";

342 else if (region1 == region2)

343 note << " (op in the same region)";

345 note << " (op in a parent region)";

347 note << " (op in a child region)";

348 else

349 note << " (op is neither in a parent nor in a child region)";

350 return;

351 }

352

354 Block *block2 = llvm::cast(operand).getOwner();

361 if (!region2) {

362 note << " (block without parent)";

363 return;

364 }

365 if (block1 == block2)

366 llvm::report_fatal_error("Internal error in dominance verification");

367 int index = std::distance(region2->begin(), block2->getIterator());

368 note << "operand defined as a block argument (block #" << index;

369 if (region1 == region2)

370 note << " in the same region)";

372 note << " in a parent region)";

374 note << " in a child region)";

375 else

376 note << " neither in a parent nor in a child region)";

377 }

378

379

380 LogicalResult

381 OperationVerifier::verifyDominanceOfContainedRegions(Operation &op,

384 while (!worklist.empty()) {

385 auto *op = worklist.pop_back_val();

386 for (auto &region : op->getRegions())

387 for (auto &block : region.getBlocks()) {

388

390 for (auto &op : block) {

391 if (isReachable) {

392

395 continue;

396

398 return failure();

399 }

400 }

401

402

403

404

405 if (verifyRecursively && op.getNumRegions() != 0) {

406

407

409 continue;

410 worklist.push_back(&op);

411 }

412 }

413 }

414 }

415

416 return success();

417 }

418

419

420

421

422

424 OperationVerifier verifier(verifyRecursively);

425 return verifier.verifyOpAndDominance(*op);

426 }

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.

union mlir::linalg::@1203::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.

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...

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 hasNoPredecessors()

Return true if this block has no predecessors.

Operation * getParentOp()

Returns the closest surrounding operation that contains this block.

This class contains all of the information necessary to report a diagnostic to the DiagnosticEngine.

Dialects are groups of MLIR operations, types and attributes, as well as behavior associated with the...

StringRef getNamespace() const

bool allowsUnknownOperations() const

Returns true if this dialect allows for unregistered operations, i.e.

A class for computing basic dominance information.

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...

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.

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.

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.

MLIRContext * getContext()

Return the context this operation is associated with.

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...

Block * getBlock()

Returns the operation block that contains this operation.

MutableArrayRef< Region > getRegions()

Returns the regions held by this operation.

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.

operand_range getOperands()

Returns an iterator on the underlying Value's.

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.

This class represents an instance of an SSA value in the MLIR system, representing a computable value...

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.

constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)

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,...