34
34
#include " mlir/Parser/Parser.h"
35
35
#include " mlir/Pass/PassManager.h"
36
36
#include " mlir/Support/FileUtilities.h"
37
+ #include " mlir/Support/ToolUtilities.h"
37
38
#include " llvm/ADT/ScopeExit.h"
38
39
#include " llvm/Support/CommandLine.h"
39
40
#include " llvm/Support/FileSystem.h"
@@ -89,6 +90,18 @@ struct Options {
89
90
cl::list<std::string> runners{" r" , cl::desc (" Use a specific set of runners" ),
90
91
cl::value_desc (" name" ),
91
92
cl::MiscFlags::CommaSeparated, cl::cat (cat)};
93
+
94
+ cl::opt<bool > verifyDiagnostics{
95
+ " verify-diagnostics" ,
96
+ cl::desc (" Check that emitted diagnostics match expected-* lines on the "
97
+ " corresponding line" ),
98
+ cl::init (false ), cl::Hidden, cl::cat (cat)};
99
+
100
+ cl::opt<bool > splitInputFile{
101
+ " split-input-file" ,
102
+ cl::desc (" Split the input file into pieces and process each "
103
+ " chunk independently" ),
104
+ cl::init (false ), cl::Hidden, cl::cat (cat)};
92
105
};
93
106
Options opts;
94
107
@@ -105,7 +118,7 @@ class Runner {
105
118
// / The name of the runner. The user can filter runners by this name, and
106
119
// / individual tests can indicate that they can or cannot run with runners
107
120
// / based on this name.
108
- std::string name;
121
+ StringAttr name;
109
122
// / The runner binary. The value of this field is resolved using
110
123
// / `findProgramByName` and stored in `binaryPath`.
111
124
std::string binary;
@@ -140,14 +153,14 @@ void RunnerSuite::addDefaultRunners() {
140
153
{
141
154
// SymbiYosys
142
155
Runner runner;
143
- runner.name = " sby" ;
156
+ runner.name = StringAttr::get (context, " sby" ) ;
144
157
runner.binary = " circt-test-runner-sby.py" ;
145
158
runners.push_back (std::move (runner));
146
159
}
147
160
{
148
161
// circt-bmc
149
162
Runner runner;
150
- runner.name = " circt-bmc" ;
163
+ runner.name = StringAttr::get (context, " circt-bmc" ) ;
151
164
runner.binary = " circt-test-runner-circt-bmc.py" ;
152
165
runner.readsMLIR = true ;
153
166
runners.push_back (std::move (runner));
@@ -209,9 +222,15 @@ class Test {
209
222
// / be the location of an MLIR op, or a line in some other source file.
210
223
LocationAttr loc;
211
224
// / Whether or not the test should be ignored
212
- bool ignore;
225
+ bool ignore = false ;
213
226
// / The user-defined attributes of this test.
214
227
DictionaryAttr attrs;
228
+ // / The set of runners that can execute this test, specified by the
229
+ // / "require_runners" array attribute in `attrs`.
230
+ SmallPtrSet<StringAttr, 1 > requiredRunners;
231
+ // / The set of runners that should be skipped for this test, specified by the
232
+ // / "exclude_runners" array attribute in `attrs`.
233
+ SmallPtrSet<StringAttr, 1 > excludedRunners;
215
234
};
216
235
217
236
// / A collection of tests discovered in some MLIR input.
@@ -227,7 +246,7 @@ class TestSuite {
227
246
228
247
TestSuite (MLIRContext *context, bool listIgnored)
229
248
: context(context), listIgnored(listIgnored) {}
230
- void discoverInModule (ModuleOp module);
249
+ LogicalResult discoverInModule (ModuleOp module);
231
250
};
232
251
} // namespace
233
252
@@ -240,20 +259,63 @@ static StringRef toString(TestKind kind) {
240
259
return " unknown" ;
241
260
}
242
261
262
+ static LogicalResult
263
+ collectRunnerFilters (DictionaryAttr attrs, StringRef attrName,
264
+ llvm::SmallPtrSetImpl<StringAttr> &names, Location loc,
265
+ StringAttr testName) {
266
+ auto attr = attrs.get (attrName);
267
+ if (!attr)
268
+ return success ();
269
+
270
+ auto arrayAttr = dyn_cast<ArrayAttr>(attr);
271
+ if (!arrayAttr)
272
+ return mlir::emitError (loc) << " `" << attrName << " ` attribute of test "
273
+ << testName << " must be an array" ;
274
+
275
+ for (auto elementAttr : arrayAttr.getValue ()) {
276
+ auto stringAttr = dyn_cast<StringAttr>(elementAttr);
277
+ if (!stringAttr)
278
+ return mlir::emitError (loc)
279
+ << " element of `" << attrName << " ` array of test " << testName
280
+ << " must be a string; got " << elementAttr;
281
+ names.insert (stringAttr);
282
+ }
283
+
284
+ return success ();
285
+ }
286
+
243
287
// / Discover all tests in an MLIR module.
244
- void TestSuite::discoverInModule (ModuleOp module) {
245
- module.walk ([&](verif::FormalOp op) {
288
+ LogicalResult TestSuite::discoverInModule (ModuleOp module) {
289
+ auto result = module.walk ([&](verif::FormalOp op) {
246
290
Test test;
247
291
test.name = op.getSymNameAttr ();
248
292
test.kind = TestKind::Formal;
249
293
test.loc = op.getLoc ();
250
294
test.attrs = op.getParametersAttr ();
251
- if (auto boolAttr = test.attrs .getAs <BoolAttr>(" ignore" ))
295
+
296
+ // Handle the `ignore` attribute.
297
+ if (auto attr = test.attrs .get (" ignore" )) {
298
+ auto boolAttr = dyn_cast<BoolAttr>(attr);
299
+ if (!boolAttr) {
300
+ op.emitError () << " `ignore` attribute of test " << test.name
301
+ << " must be a boolean" ;
302
+ return WalkResult::interrupt ();
303
+ }
252
304
test.ignore = boolAttr.getValue ();
253
- else
254
- test.ignore = false ;
305
+ }
306
+
307
+ // Handle the `require_runners` and `exclude_runners` attributes.
308
+ if (failed (collectRunnerFilters (test.attrs , " require_runners" ,
309
+ test.requiredRunners , test.loc ,
310
+ test.name )) ||
311
+ failed (collectRunnerFilters (test.attrs , " exclude_runners" ,
312
+ test.excludedRunners , test.loc , test.name )))
313
+ return WalkResult::interrupt ();
314
+
255
315
tests.push_back (std::move (test));
316
+ return WalkResult::advance ();
256
317
});
318
+ return failure (result.wasInterrupted ());
257
319
}
258
320
259
321
// ===----------------------------------------------------------------------===//
@@ -272,7 +334,7 @@ static LogicalResult listRunners(RunnerSuite &suite) {
272
334
273
335
for (auto &runner : suite.runners ) {
274
336
auto &os = output->os ();
275
- os << runner.name ;
337
+ os << runner.name . getValue () ;
276
338
if (runner.ignore )
277
339
os << " ignored" ;
278
340
else if (runner.available )
@@ -336,31 +398,20 @@ static LogicalResult listTests(TestSuite &suite) {
336
398
return success ();
337
399
}
338
400
339
- // / Entry point for the circt-test tool. At this point an MLIRContext is
340
- // / available, all dialects have been registered, and all command line options
341
- // / have been parsed.
342
- static LogicalResult execute (MLIRContext *context) {
343
- SourceMgr srcMgr;
344
- SourceMgrDiagnosticHandler handler (srcMgr, context);
345
-
346
- // Discover all available test runners.
347
- RunnerSuite runnerSuite (context);
348
- runnerSuite.addDefaultRunners ();
349
- if (failed (runnerSuite.resolve ()))
350
- return failure ();
351
-
352
- // List all runners and exit if requested.
353
- if (opts.listRunners )
354
- return listRunners (runnerSuite);
355
-
356
- // Parse the input file.
357
- auto module = parseSourceFile<ModuleOp>(opts.inputFilename , srcMgr, context);
401
+ // / Called once the suite of available runners has been determined and a module
402
+ // / has been parsed. If the `--split-input-file` option is set, this function is
403
+ // / called once for each split of the input file.
404
+ static LogicalResult executeWithHandler (MLIRContext *context,
405
+ RunnerSuite &runnerSuite,
406
+ SourceMgr &srcMgr) {
407
+ auto module = parseSourceFile<ModuleOp>(srcMgr, context);
358
408
if (!module)
359
409
return failure ();
360
410
361
411
// Discover all tests in the input.
362
412
TestSuite suite (context, opts.listIgnored );
363
- suite.discoverInModule (*module);
413
+ if (failed (suite.discoverInModule (*module)))
414
+ return failure ();
364
415
if (suite.tests .empty ()) {
365
416
llvm::errs () << " no tests discovered\n " ;
366
417
return success ();
@@ -417,12 +468,16 @@ static LogicalResult execute(MLIRContext *context) {
417
468
for (auto &candidate : runnerSuite.runners ) {
418
469
if (candidate.ignore || !candidate.available )
419
470
continue ;
471
+ if (!test.requiredRunners .empty () &&
472
+ !test.requiredRunners .contains (candidate.name ))
473
+ continue ;
474
+ if (test.excludedRunners .contains (candidate.name ))
475
+ continue ;
420
476
runner = &candidate;
421
477
break ;
422
478
}
423
479
if (!runner) {
424
480
++numUnsupported;
425
- mlir::emitError (test.loc ) << " no runner for test " << test.name ;
426
481
return ;
427
482
}
428
483
@@ -490,6 +545,7 @@ static LogicalResult execute(MLIRContext *context) {
490
545
auto d = mlir::emitError (test.loc )
491
546
<< " test " << test.name .getValue () << " failed" ;
492
547
d.attachNote () << " see `" << logPath << " `" ;
548
+ d.attachNote () << " executed with " << runner->name .getValue ();
493
549
} else {
494
550
++numPassed;
495
551
}
@@ -517,6 +573,52 @@ static LogicalResult execute(MLIRContext *context) {
517
573
return success (numFailed == 0 );
518
574
}
519
575
576
+ // / Entry point for the circt-test tool. At this point an MLIRContext is
577
+ // / available, all dialects have been registered, and all command line options
578
+ // / have been parsed.
579
+ static LogicalResult execute (MLIRContext *context) {
580
+ // Discover all available test runners.
581
+ RunnerSuite runnerSuite (context);
582
+ runnerSuite.addDefaultRunners ();
583
+ if (failed (runnerSuite.resolve ()))
584
+ return failure ();
585
+
586
+ // List all runners and exit if requested.
587
+ if (opts.listRunners )
588
+ return listRunners (runnerSuite);
589
+
590
+ // Read the input file.
591
+ auto input = llvm::MemoryBuffer::getFileOrSTDIN (opts.inputFilename );
592
+ if (input.getError ()) {
593
+ WithColor::error () << " could not open input file " << opts.inputFilename ;
594
+ return failure ();
595
+ }
596
+
597
+ // Process the input file. If requested by the user, split the input file and
598
+ // process each chunk separately. This is useful for verifying diagnostics.
599
+ auto processBuffer = [&](std::unique_ptr<llvm::MemoryBuffer> chunk,
600
+ raw_ostream &) {
601
+ SourceMgr srcMgr;
602
+ srcMgr.AddNewSourceBuffer (std::move (chunk), llvm::SMLoc ());
603
+
604
+ // Call `executeWithHandler` with either the regular diagnostic handler, or,
605
+ // if `--verify-diagnostics` is set, with the verifying handler.
606
+ if (opts.verifyDiagnostics ) {
607
+ SourceMgrDiagnosticVerifierHandler handler (srcMgr, context);
608
+ context->printOpOnDiagnostic (false );
609
+ (void )executeWithHandler (context, runnerSuite, srcMgr);
610
+ return handler.verify ();
611
+ }
612
+
613
+ SourceMgrDiagnosticHandler handler (srcMgr, context);
614
+ return executeWithHandler (context, runnerSuite, srcMgr);
615
+ };
616
+
617
+ return mlir::splitAndProcessBuffer (
618
+ std::move (*input), processBuffer, llvm::outs (),
619
+ opts.splitInputFile ? mlir::kDefaultSplitMarker : " " );
620
+ }
621
+
520
622
int main (int argc, char **argv) {
521
623
InitLLVM y (argc, argv);
522
624
0 commit comments