Implement a simple IR verifier, including support for custom ops adding their
own requirements.
PiperOrigin-RevId: 203497491
diff --git a/lib/IR/Verifier.cpp b/lib/IR/Verifier.cpp
new file mode 100644
index 0000000..ffc87aa
--- /dev/null
+++ b/lib/IR/Verifier.cpp
@@ -0,0 +1,155 @@
+//===- Verifier.cpp - MLIR Verifier Implementation ------------------------===//
+//
+// Copyright 2019 The MLIR Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// =============================================================================
+//
+// This file implements the verify() methods on the various IR types, performing
+// (potentially expensive) checks on the holistic structure of the code. This
+// can be used for detecting bugs in compiler transformations and hand written
+// .mlir files.
+//
+// The checks in this file are only for things that can occur as part of IR
+// transformations: e.g. violation of dominance information, malformed operation
+// attributes, etc. MLIR supports transformations moving IR through locally
+// invalid states (e.g. unlinking an instruction from an instruction before
+// re-inserting it in a new place), but each transformation must complete with
+// the IR in a valid form.
+//
+// This should not check for things that are always wrong by construction (e.g.
+// affine maps or other immutable structures that are incorrect), because those
+// are not mutable and can be checked at time of construction.
+//
+//===----------------------------------------------------------------------===//
+
+#include "mlir/IR/CFGFunction.h"
+#include "mlir/IR/MLFunction.h"
+#include "mlir/IR/Module.h"
+#include "mlir/IR/OperationSet.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/raw_ostream.h"
+using namespace mlir;
+
+template <typename T>
+static void failure(const Twine &message, const T &value) {
+ // Print the error message and flush the stream in case printing the value
+ // causes a crash.
+ llvm::errs() << "MLIR verification failure: " << message << "\n";
+ llvm::errs().flush();
+ value.dump();
+}
+
+//===----------------------------------------------------------------------===//
+// CFG Functions
+//===----------------------------------------------------------------------===//
+
+namespace {
+class CFGFuncVerifier {
+public:
+ const CFGFunction &fn;
+ OperationSet &operationSet;
+
+ CFGFuncVerifier(const CFGFunction &fn)
+ : fn(fn), operationSet(OperationSet::get(fn.getContext())) {}
+
+ void verify();
+ void verifyBlock(const BasicBlock &block);
+ void verifyTerminator(const TerminatorInst &term);
+ void verifyOperation(const OperationInst &inst);
+};
+} // end anonymous namespace
+
+void CFGFuncVerifier::verify() {
+ // TODO: Lots to be done here, including verifying dominance information when
+ // we have uses and defs.
+
+ for (auto &block : fn) {
+ verifyBlock(block);
+ }
+}
+
+void CFGFuncVerifier::verifyBlock(const BasicBlock &block) {
+ if (!block.getTerminator())
+ failure("basic block with no terminator", block);
+ verifyTerminator(*block.getTerminator());
+
+ for (auto &inst : block) {
+ verifyOperation(inst);
+ }
+}
+
+void CFGFuncVerifier::verifyTerminator(const TerminatorInst &term) {
+ if (term.getFunction() != &fn)
+ failure("terminator in the wrong function", term);
+
+ // TODO: Check that operands are structurally ok.
+ // TODO: Check that successors are in the right function.
+}
+
+void CFGFuncVerifier::verifyOperation(const OperationInst &inst) {
+ if (inst.getFunction() != &fn)
+ failure("operation in the wrong function", inst);
+
+ // TODO: Check that operands are structurally ok.
+
+ // See if we can get operation info for this.
+ if (auto *opInfo = inst.getAbstractOperation(fn.getContext())) {
+ if (auto errorMessage = opInfo->verifyInvariants(&inst))
+ failure(errorMessage, inst);
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// ML Functions
+//===----------------------------------------------------------------------===//
+
+namespace {
+class MLFuncVerifier {
+public:
+ const MLFunction &fn;
+
+ MLFuncVerifier(const MLFunction &fn) : fn(fn) {}
+
+ void verify() {
+ // TODO.
+ }
+};
+} // end anonymous namespace
+
+//===----------------------------------------------------------------------===//
+// Entrypoints
+//===----------------------------------------------------------------------===//
+
+/// Perform (potentially expensive) checks of invariants, used to detect
+/// compiler bugs. This aborts on failure.
+void Function::verify() const {
+ switch (getKind()) {
+ case Kind::ExtFunc:
+ // No body, nothing can be wrong here.
+ break;
+ case Kind::CFGFunc:
+ return CFGFuncVerifier(*cast<CFGFunction>(this)).verify();
+ case Kind::MLFunc:
+ return MLFuncVerifier(*cast<MLFunction>(this)).verify();
+ }
+}
+
+/// Perform (potentially expensive) checks of invariants, used to detect
+/// compiler bugs. This aborts on failure.
+void Module::verify() const {
+ /// Check that each function is correct.
+ for (auto fn : functionList) {
+ fn->verify();
+ }
+}