Skip to content

Commit 1cec5ff

Browse files
authored
[mlir] implement -verify-diagnostics=only-expected (#135131)
This PR implements `verify-diagnostics=only-expected` which is a "best effort" verification - i.e., `unexpected`s and `near-misses` will not be considered failures. The purpose is to enable narrowly scoped checking of verification remarks (just as we have for lit where only a subset of lines get `CHECK`ed).
1 parent 1331f17 commit 1cec5ff

File tree

9 files changed

+157
-39
lines changed

9 files changed

+157
-39
lines changed

mlir/examples/transform-opt/mlir-transform-opt.cpp

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,22 @@ struct MlirTransformOptCLOptions {
3838
cl::desc("Allow operations coming from an unregistered dialect"),
3939
cl::init(false)};
4040

41-
cl::opt<bool> verifyDiagnostics{
42-
"verify-diagnostics",
43-
cl::desc("Check that emitted diagnostics match expected-* lines "
44-
"on the corresponding line"),
45-
cl::init(false)};
41+
cl::opt<mlir::SourceMgrDiagnosticVerifierHandler::Level> verifyDiagnostics{
42+
"verify-diagnostics", llvm::cl::ValueOptional,
43+
cl::desc("Check that emitted diagnostics match expected-* lines on the "
44+
"corresponding line"),
45+
cl::values(
46+
clEnumValN(
47+
mlir::SourceMgrDiagnosticVerifierHandler::Level::All, "all",
48+
"Check all diagnostics (expected, unexpected, near-misses)"),
49+
// Implicit value: when passed with no arguments, e.g.
50+
// `--verify-diagnostics` or `--verify-diagnostics=`.
51+
clEnumValN(
52+
mlir::SourceMgrDiagnosticVerifierHandler::Level::All, "",
53+
"Check all diagnostics (expected, unexpected, near-misses)"),
54+
clEnumValN(
55+
mlir::SourceMgrDiagnosticVerifierHandler::Level::OnlyExpected,
56+
"only-expected", "Check only expected diagnostics"))};
4657

4758
cl::opt<std::string> payloadFilename{cl::Positional, cl::desc("<input file>"),
4859
cl::init("-")};
@@ -102,12 +113,17 @@ class DiagnosticHandlerWrapper {
102113

103114
/// Constructs the diagnostic handler of the specified kind of the given
104115
/// source manager and context.
105-
DiagnosticHandlerWrapper(Kind kind, llvm::SourceMgr &mgr,
106-
mlir::MLIRContext *context) {
107-
if (kind == Kind::EmitDiagnostics)
116+
DiagnosticHandlerWrapper(
117+
Kind kind, llvm::SourceMgr &mgr, mlir::MLIRContext *context,
118+
std::optional<mlir::SourceMgrDiagnosticVerifierHandler::Level> level =
119+
{}) {
120+
if (kind == Kind::EmitDiagnostics) {
108121
handler = new mlir::SourceMgrDiagnosticHandler(mgr, context);
109-
else
110-
handler = new mlir::SourceMgrDiagnosticVerifierHandler(mgr, context);
122+
} else {
123+
assert(level.has_value() && "expected level");
124+
handler =
125+
new mlir::SourceMgrDiagnosticVerifierHandler(mgr, context, *level);
126+
}
111127
}
112128

113129
/// This object is non-copyable but movable.
@@ -150,7 +166,9 @@ class TransformSourceMgr {
150166
public:
151167
/// Constructs the source manager indicating whether diagnostic messages will
152168
/// be verified later on.
153-
explicit TransformSourceMgr(bool verifyDiagnostics)
169+
explicit TransformSourceMgr(
170+
std::optional<mlir::SourceMgrDiagnosticVerifierHandler::Level>
171+
verifyDiagnostics)
154172
: verifyDiagnostics(verifyDiagnostics) {}
155173

156174
/// Deconstructs the source manager. Note that `checkResults` must have been
@@ -179,7 +197,8 @@ class TransformSourceMgr {
179197
// verification needs to happen and store it.
180198
if (verifyDiagnostics) {
181199
diagHandlers.emplace_back(
182-
DiagnosticHandlerWrapper::Kind::VerifyDiagnostics, mgr, &context);
200+
DiagnosticHandlerWrapper::Kind::VerifyDiagnostics, mgr, &context,
201+
verifyDiagnostics);
183202
} else {
184203
diagHandlers.emplace_back(DiagnosticHandlerWrapper::Kind::EmitDiagnostics,
185204
mgr, &context);
@@ -204,7 +223,8 @@ class TransformSourceMgr {
204223

205224
private:
206225
/// Indicates whether diagnostic message verification is requested.
207-
const bool verifyDiagnostics;
226+
const std::optional<mlir::SourceMgrDiagnosticVerifierHandler::Level>
227+
verifyDiagnostics;
208228

209229
/// Indicates that diagnostic message verification has taken place, and the
210230
/// deconstruction is therefore safe.
@@ -248,7 +268,9 @@ static llvm::LogicalResult processPayloadBuffer(
248268
context.allowUnregisteredDialects(clOptions->allowUnregisteredDialects);
249269
mlir::ParserConfig config(&context);
250270
TransformSourceMgr sourceMgr(
251-
/*verifyDiagnostics=*/clOptions->verifyDiagnostics);
271+
/*verifyDiagnostics=*/clOptions->verifyDiagnostics.getNumOccurrences()
272+
? std::optional{clOptions->verifyDiagnostics.getValue()}
273+
: std::nullopt);
252274

253275
// Parse the input buffer that will be used as transform payload.
254276
mlir::OwningOpRef<mlir::Operation *> payloadRoot =

mlir/include/mlir/IR/Diagnostics.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -626,9 +626,12 @@ struct SourceMgrDiagnosticVerifierHandlerImpl;
626626
/// corresponding line of the source file.
627627
class SourceMgrDiagnosticVerifierHandler : public SourceMgrDiagnosticHandler {
628628
public:
629+
enum class Level { None = 0, All, OnlyExpected };
629630
SourceMgrDiagnosticVerifierHandler(llvm::SourceMgr &srcMgr, MLIRContext *ctx,
630-
raw_ostream &out);
631-
SourceMgrDiagnosticVerifierHandler(llvm::SourceMgr &srcMgr, MLIRContext *ctx);
631+
raw_ostream &out,
632+
Level level = Level::All);
633+
SourceMgrDiagnosticVerifierHandler(llvm::SourceMgr &srcMgr, MLIRContext *ctx,
634+
Level level = Level::All);
632635
~SourceMgrDiagnosticVerifierHandler();
633636

634637
/// Returns the status of the handler and verifies that all expected

mlir/include/mlir/Tools/mlir-opt/MlirOptMain.h

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -185,11 +185,20 @@ class MlirOptMainConfig {
185185

186186
/// Set whether to check that emitted diagnostics match `expected-*` lines on
187187
/// the corresponding line. This is meant for implementing diagnostic tests.
188-
MlirOptMainConfig &verifyDiagnostics(bool verify) {
188+
MlirOptMainConfig &
189+
verifyDiagnostics(SourceMgrDiagnosticVerifierHandler::Level verify) {
189190
verifyDiagnosticsFlag = verify;
190191
return *this;
191192
}
192-
bool shouldVerifyDiagnostics() const { return verifyDiagnosticsFlag; }
193+
194+
bool shouldVerifyDiagnostics() const {
195+
return verifyDiagnosticsFlag !=
196+
SourceMgrDiagnosticVerifierHandler::Level::None;
197+
}
198+
199+
SourceMgrDiagnosticVerifierHandler::Level verifyDiagnosticsLevel() const {
200+
return verifyDiagnosticsFlag;
201+
}
193202

194203
/// Set whether to run the verifier after each transformation pass.
195204
MlirOptMainConfig &verifyPasses(bool verify) {
@@ -276,7 +285,8 @@ class MlirOptMainConfig {
276285

277286
/// Set whether to check that emitted diagnostics match `expected-*` lines on
278287
/// the corresponding line. This is meant for implementing diagnostic tests.
279-
bool verifyDiagnosticsFlag = false;
288+
SourceMgrDiagnosticVerifierHandler::Level verifyDiagnosticsFlag =
289+
SourceMgrDiagnosticVerifierHandler::Level::None;
280290

281291
/// Run the verifier after each transformation pass.
282292
bool verifyPassesFlag = true;

mlir/lib/IR/Diagnostics.cpp

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -661,7 +661,9 @@ struct ExpectedDiag {
661661
};
662662

663663
struct SourceMgrDiagnosticVerifierHandlerImpl {
664-
SourceMgrDiagnosticVerifierHandlerImpl() : status(success()) {}
664+
SourceMgrDiagnosticVerifierHandlerImpl(
665+
SourceMgrDiagnosticVerifierHandler::Level level)
666+
: status(success()), level(level) {}
665667

666668
/// Returns the expected diagnostics for the given source file.
667669
std::optional<MutableArrayRef<ExpectedDiag>>
@@ -672,6 +674,10 @@ struct SourceMgrDiagnosticVerifierHandlerImpl {
672674
computeExpectedDiags(raw_ostream &os, llvm::SourceMgr &mgr,
673675
const llvm::MemoryBuffer *buf);
674676

677+
SourceMgrDiagnosticVerifierHandler::Level getVerifyLevel() const {
678+
return level;
679+
}
680+
675681
/// The current status of the verifier.
676682
LogicalResult status;
677683

@@ -685,6 +691,10 @@ struct SourceMgrDiagnosticVerifierHandlerImpl {
685691
llvm::Regex expected =
686692
llvm::Regex("expected-(error|note|remark|warning)(-re)? "
687693
"*(@([+-][0-9]+|above|below|unknown))? *{{(.*)}}$");
694+
695+
/// Verification level.
696+
SourceMgrDiagnosticVerifierHandler::Level level =
697+
SourceMgrDiagnosticVerifierHandler::Level::All;
688698
};
689699
} // namespace detail
690700
} // namespace mlir
@@ -803,9 +813,9 @@ SourceMgrDiagnosticVerifierHandlerImpl::computeExpectedDiags(
803813
}
804814

805815
SourceMgrDiagnosticVerifierHandler::SourceMgrDiagnosticVerifierHandler(
806-
llvm::SourceMgr &srcMgr, MLIRContext *ctx, raw_ostream &out)
816+
llvm::SourceMgr &srcMgr, MLIRContext *ctx, raw_ostream &out, Level level)
807817
: SourceMgrDiagnosticHandler(srcMgr, ctx, out),
808-
impl(new SourceMgrDiagnosticVerifierHandlerImpl()) {
818+
impl(new SourceMgrDiagnosticVerifierHandlerImpl(level)) {
809819
// Compute the expected diagnostics for each of the current files in the
810820
// source manager.
811821
for (unsigned i = 0, e = mgr.getNumBuffers(); i != e; ++i)
@@ -823,8 +833,8 @@ SourceMgrDiagnosticVerifierHandler::SourceMgrDiagnosticVerifierHandler(
823833
}
824834

825835
SourceMgrDiagnosticVerifierHandler::SourceMgrDiagnosticVerifierHandler(
826-
llvm::SourceMgr &srcMgr, MLIRContext *ctx)
827-
: SourceMgrDiagnosticVerifierHandler(srcMgr, ctx, llvm::errs()) {}
836+
llvm::SourceMgr &srcMgr, MLIRContext *ctx, Level level)
837+
: SourceMgrDiagnosticVerifierHandler(srcMgr, ctx, llvm::errs(), level) {}
828838

829839
SourceMgrDiagnosticVerifierHandler::~SourceMgrDiagnosticVerifierHandler() {
830840
// Ensure that all expected diagnostics were handled.
@@ -898,6 +908,9 @@ void SourceMgrDiagnosticVerifierHandler::process(LocationAttr loc,
898908
}
899909
}
900910

911+
if (impl->getVerifyLevel() == Level::OnlyExpected)
912+
return;
913+
901914
// Otherwise, emit an error for the near miss.
902915
if (nearMiss)
903916
mgr.PrintMessage(os, nearMiss->fileLoc, llvm::SourceMgr::DK_Error,

mlir/lib/Tools/mlir-opt/MlirOptMain.cpp

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -163,11 +163,26 @@ struct MlirOptMainConfigCLOptions : public MlirOptMainConfig {
163163
cl::desc("Split marker to use for merging the ouput"),
164164
cl::location(outputSplitMarkerFlag), cl::init(kDefaultSplitMarker));
165165

166-
static cl::opt<bool, /*ExternalStorage=*/true> verifyDiagnostics(
167-
"verify-diagnostics",
168-
cl::desc("Check that emitted diagnostics match "
169-
"expected-* lines on the corresponding line"),
170-
cl::location(verifyDiagnosticsFlag), cl::init(false));
166+
static cl::opt<SourceMgrDiagnosticVerifierHandler::Level,
167+
/*ExternalStorage=*/true>
168+
verifyDiagnostics{
169+
"verify-diagnostics", llvm::cl::ValueOptional,
170+
cl::desc("Check that emitted diagnostics match expected-* lines on "
171+
"the corresponding line"),
172+
cl::location(verifyDiagnosticsFlag),
173+
cl::values(
174+
clEnumValN(SourceMgrDiagnosticVerifierHandler::Level::All,
175+
"all",
176+
"Check all diagnostics (expected, unexpected, "
177+
"near-misses)"),
178+
// Implicit value: when passed with no arguments, e.g.
179+
// `--verify-diagnostics` or `--verify-diagnostics=`.
180+
clEnumValN(SourceMgrDiagnosticVerifierHandler::Level::All, "",
181+
"Check all diagnostics (expected, unexpected, "
182+
"near-misses)"),
183+
clEnumValN(
184+
SourceMgrDiagnosticVerifierHandler::Level::OnlyExpected,
185+
"only-expected", "Check only expected diagnostics"))};
171186

172187
static cl::opt<bool, /*ExternalStorage=*/true> verifyPasses(
173188
"verify-each",
@@ -537,7 +552,8 @@ static LogicalResult processBuffer(raw_ostream &os,
537552
return performActions(os, sourceMgr, &context, config);
538553
}
539554

540-
SourceMgrDiagnosticVerifierHandler sourceMgrHandler(*sourceMgr, &context);
555+
SourceMgrDiagnosticVerifierHandler sourceMgrHandler(
556+
*sourceMgr, &context, config.verifyDiagnosticsLevel());
541557

542558
// Do any processing requested by command line flags. We don't care whether
543559
// these actions succeed or fail, we only care what diagnostics they produce

mlir/lib/Tools/mlir-translate/MlirTranslateMain.cpp

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,23 @@ LogicalResult mlir::mlirTranslateMain(int argc, char **argv,
7272
"default marker and process each chunk independently"),
7373
llvm::cl::init("")};
7474

75-
static llvm::cl::opt<bool> verifyDiagnostics(
76-
"verify-diagnostics",
77-
llvm::cl::desc("Check that emitted diagnostics match "
78-
"expected-* lines on the corresponding line"),
79-
llvm::cl::init(false));
75+
static llvm::cl::opt<SourceMgrDiagnosticVerifierHandler::Level>
76+
verifyDiagnostics{
77+
"verify-diagnostics", llvm::cl::ValueOptional,
78+
llvm::cl::desc("Check that emitted diagnostics match expected-* "
79+
"lines on the corresponding line"),
80+
llvm::cl::values(
81+
clEnumValN(
82+
SourceMgrDiagnosticVerifierHandler::Level::All, "all",
83+
"Check all diagnostics (expected, unexpected, near-misses)"),
84+
// Implicit value: when passed with no arguments, e.g.
85+
// `--verify-diagnostics` or `--verify-diagnostics=`.
86+
clEnumValN(
87+
SourceMgrDiagnosticVerifierHandler::Level::All, "",
88+
"Check all diagnostics (expected, unexpected, near-misses)"),
89+
clEnumValN(
90+
SourceMgrDiagnosticVerifierHandler::Level::OnlyExpected,
91+
"only-expected", "Check only expected diagnostics"))};
8092

8193
static llvm::cl::opt<bool> errorDiagnosticsOnly(
8294
"error-diagnostics-only",
@@ -149,17 +161,17 @@ LogicalResult mlir::mlirTranslateMain(int argc, char **argv,
149161

150162
MLIRContext context;
151163
context.allowUnregisteredDialects(allowUnregisteredDialects);
152-
context.printOpOnDiagnostic(!verifyDiagnostics);
164+
context.printOpOnDiagnostic(verifyDiagnostics.getNumOccurrences() == 0);
153165
auto sourceMgr = std::make_shared<llvm::SourceMgr>();
154166
sourceMgr->AddNewSourceBuffer(std::move(ownedBuffer), SMLoc());
155167

156-
if (verifyDiagnostics) {
168+
if (verifyDiagnostics.getNumOccurrences()) {
157169
// In the diagnostic verification flow, we ignore whether the
158170
// translation failed (in most cases, it is expected to fail) and we do
159171
// not filter non-error diagnostics even if `errorDiagnosticsOnly` is
160172
// set. Instead, we check if the diagnostics were produced as expected.
161-
SourceMgrDiagnosticVerifierHandler sourceMgrHandler(*sourceMgr,
162-
&context);
173+
SourceMgrDiagnosticVerifierHandler sourceMgrHandler(
174+
*sourceMgr, &context, verifyDiagnostics);
163175
(void)(*translationRequested)(sourceMgr, os, &context);
164176
result = sourceMgrHandler.verify();
165177
} else if (errorDiagnosticsOnly) {

mlir/test/Pass/full_diagnostics.mlir

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// RUN: mlir-opt %s -pass-pipeline='builtin.module(func.func(test-pass-failure{gen-diagnostics}))' -verify-diagnostics
2+
// RUN: mlir-opt %s -pass-pipeline='builtin.module(func.func(test-pass-failure{gen-diagnostics}))' -verify-diagnostics=all
23

34
// Test that all errors are reported.
45
// expected-error@below {{illegal operation}}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// RUN: mlir-opt %s -pass-pipeline='builtin.module(func.func(test-pass-failure{gen-diagnostics}))' -verify-diagnostics=only-expected
2+
3+
// Test that only expected errors are reported.
4+
// reports {{illegal operation}} but unchecked
5+
func.func @TestAlwaysIllegalOperationPass1() {
6+
return
7+
}
8+
9+
// expected-error@+1 {{illegal operation}}
10+
func.func @TestAlwaysIllegalOperationPass2() {
11+
return
12+
}
13+
14+
// reports {{illegal operation}} but unchecked
15+
func.func @TestAlwaysIllegalOperationPass3() {
16+
return
17+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Check that verify-diagnostics=only-expected passes with only one actual `expected-error`
2+
// RUN: mlir-translate %s --allow-unregistered-dialect -verify-diagnostics=only-expected -split-input-file -mlir-to-llvmir
3+
4+
// Check that verify-diagnostics=all fails because we're missing two `expected-error`
5+
// RUN: not mlir-translate %s --allow-unregistered-dialect -verify-diagnostics=all -split-input-file -mlir-to-llvmir 2>&1 | FileCheck %s --check-prefix=CHECK-VERIFY-ALL
6+
// CHECK-VERIFY-ALL: unexpected error: cannot be converted to LLVM IR: missing `LLVMTranslationDialectInterface` registration for dialect for op: simple.terminator1
7+
// CHECK-VERIFY-ALL: unexpected error: cannot be converted to LLVM IR: missing `LLVMTranslationDialectInterface` registration for dialect for op: simple.terminator3
8+
9+
llvm.func @trivial() {
10+
"simple.terminator1"() : () -> ()
11+
}
12+
13+
// -----
14+
15+
llvm.func @trivial() {
16+
// expected-error @+1 {{cannot be converted to LLVM IR: missing `LLVMTranslationDialectInterface` registration for dialect for op: simple.terminator2}}
17+
"simple.terminator2"() : () -> ()
18+
}
19+
20+
// -----
21+
22+
llvm.func @trivial() {
23+
"simple.terminator3"() : () -> ()
24+
}

0 commit comments

Comments
 (0)