Skip to content

Commit 50f539c

Browse files
authored
[MLIR] Add remark flags to mlir-opt (#156825)
1 parent da82d72 commit 50f539c

File tree

11 files changed

+300
-18
lines changed

11 files changed

+300
-18
lines changed

mlir/include/mlir/IR/Remarks.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ namespace mlir::remark {
2929
/// Define an the set of categories to accept. By default none are, the provided
3030
/// regex matches against the category names for each kind of remark.
3131
struct RemarkCategories {
32-
std::optional<std::string> passed, missed, analysis, failed;
32+
std::optional<std::string> all, passed, missed, analysis, failed;
3333
};
3434

3535
/// Categories describe the outcome of an transformation, not the mechanics of

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

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ enum class VerbosityLevel {
3838
ErrorsWarningsAndRemarks
3939
};
4040

41+
using RemarkFormat = enum {
42+
REMARK_FORMAT_STDOUT,
43+
REMARK_FORMAT_YAML,
44+
REMARK_FORMAT_BITSTREAM,
45+
};
46+
4147
/// Configuration options for the mlir-opt tool.
4248
/// This is intended to help building tools like mlir-opt by collecting the
4349
/// supported options.
@@ -221,15 +227,53 @@ class MlirOptMainConfig {
221227
}
222228
bool shouldVerifyRoundtrip() const { return verifyRoundtripFlag; }
223229

230+
/// Checks if any remark filters are set.
231+
bool shouldEmitRemarks() const {
232+
// Emit all remarks only when no filters are specified.
233+
const bool hasFilters =
234+
!getRemarksAllFilter().empty() || !getRemarksPassedFilter().empty() ||
235+
!getRemarksFailedFilter().empty() ||
236+
!getRemarksMissedFilter().empty() || !getRemarksAnalyseFilter().empty();
237+
return hasFilters;
238+
}
239+
224240
/// Reproducer file generation (no crash required).
225241
StringRef getReproducerFilename() const { return generateReproducerFileFlag; }
226242

243+
/// Set the reproducer output filename
244+
RemarkFormat getRemarkFormat() const { return remarkFormatFlag; }
245+
/// Set the remark format to use.
246+
std::string getRemarksAllFilter() const { return remarksAllFilterFlag; }
247+
/// Set the remark output file.
248+
std::string getRemarksOutputFile() const { return remarksOutputFileFlag; }
249+
/// Set the remark passed filters.
250+
std::string getRemarksPassedFilter() const { return remarksPassedFilterFlag; }
251+
/// Set the remark failed filters.
252+
std::string getRemarksFailedFilter() const { return remarksFailedFilterFlag; }
253+
/// Set the remark missed filters.
254+
std::string getRemarksMissedFilter() const { return remarksMissedFilterFlag; }
255+
/// Set the remark analyse filters.
256+
std::string getRemarksAnalyseFilter() const {
257+
return remarksAnalyseFilterFlag;
258+
}
259+
227260
protected:
228261
/// Allow operation with no registered dialects.
229262
/// This option is for convenience during testing only and discouraged in
230263
/// general.
231264
bool allowUnregisteredDialectsFlag = false;
232265

266+
/// Remark format
267+
RemarkFormat remarkFormatFlag;
268+
/// Remark file to output to
269+
std::string remarksOutputFileFlag = "";
270+
/// Remark filters
271+
std::string remarksAllFilterFlag = "";
272+
std::string remarksPassedFilterFlag = "";
273+
std::string remarksFailedFilterFlag = "";
274+
std::string remarksMissedFilterFlag = "";
275+
std::string remarksAnalyseFilterFlag = "";
276+
233277
/// Configuration for the debugging hooks.
234278
tracing::DebugConfig debugConfig;
235279

mlir/lib/IR/Remarks.cpp

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -248,17 +248,56 @@ RemarkEngine::initialize(std::unique_ptr<MLIRRemarkStreamerBase> streamer,
248248
return success();
249249
}
250250

251+
/// Returns true if filter is already anchored like ^...$
252+
static bool isAnchored(llvm::StringRef s) {
253+
s = s.trim();
254+
return s.starts_with("^") && s.ends_with("$"); // note: startswith/endswith
255+
}
256+
257+
/// Anchor the entire pattern so it matches the whole string.
258+
static std::string anchorWhole(llvm::StringRef filter) {
259+
if (isAnchored(filter))
260+
return filter.str();
261+
return (llvm::Twine("^(") + filter + ")$").str();
262+
}
263+
264+
/// Build a combined filter from cats.all and a category-specific pattern.
265+
/// If neither is present, return std::nullopt. Otherwise "(all|specific)"
266+
/// and anchor once. Also validate before returning.
267+
static std::optional<llvm::Regex>
268+
buildFilter(const mlir::remark::RemarkCategories &cats,
269+
const std::optional<std::string> &specific) {
270+
llvm::SmallVector<llvm::StringRef, 2> parts;
271+
if (cats.all && !cats.all->empty())
272+
parts.emplace_back(*cats.all);
273+
if (specific && !specific->empty())
274+
parts.emplace_back(*specific);
275+
276+
if (parts.empty())
277+
return std::nullopt;
278+
279+
std::string joined = llvm::join(parts, "|");
280+
std::string anchored = anchorWhole(joined);
281+
282+
llvm::Regex rx(anchored);
283+
std::string err;
284+
if (!rx.isValid(err))
285+
return std::nullopt;
286+
287+
return rx;
288+
}
289+
251290
RemarkEngine::RemarkEngine(bool printAsEmitRemarks,
252291
const RemarkCategories &cats)
253292
: printAsEmitRemarks(printAsEmitRemarks) {
254293
if (cats.passed)
255-
passedFilter = llvm::Regex(cats.passed.value());
294+
passedFilter = buildFilter(cats, cats.passed);
256295
if (cats.missed)
257-
missFilter = llvm::Regex(cats.missed.value());
296+
missFilter = buildFilter(cats, cats.missed);
258297
if (cats.analysis)
259-
analysisFilter = llvm::Regex(cats.analysis.value());
298+
analysisFilter = buildFilter(cats, cats.analysis);
260299
if (cats.failed)
261-
failedFilter = llvm::Regex(cats.failed.value());
300+
failedFilter = buildFilter(cats, cats.failed);
262301
}
263302

264303
llvm::LogicalResult mlir::remark::enableOptimizationRemarks(

mlir/lib/Tools/mlir-opt/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ add_mlir_library(MLIROptLib
1313
MLIRPluginsLib
1414
MLIRSupport
1515
MLIRIRDL
16+
MLIRRemarkStreamer
1617
)

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

Lines changed: 99 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,19 @@
2323
#include "mlir/IR/Diagnostics.h"
2424
#include "mlir/IR/Location.h"
2525
#include "mlir/IR/MLIRContext.h"
26+
#include "mlir/IR/Remarks.h"
2627
#include "mlir/Parser/Parser.h"
2728
#include "mlir/Pass/PassManager.h"
2829
#include "mlir/Pass/PassRegistry.h"
30+
#include "mlir/Remark/RemarkStreamer.h"
2931
#include "mlir/Support/FileUtilities.h"
3032
#include "mlir/Support/Timing.h"
3133
#include "mlir/Support/ToolUtilities.h"
3234
#include "mlir/Tools/ParseUtilities.h"
3335
#include "mlir/Tools/Plugins/DialectPlugin.h"
3436
#include "mlir/Tools/Plugins/PassPlugin.h"
3537
#include "llvm/ADT/StringRef.h"
38+
#include "llvm/Remarks/RemarkFormat.h"
3639
#include "llvm/Support/CommandLine.h"
3740
#include "llvm/Support/InitLLVM.h"
3841
#include "llvm/Support/LogicalResult.h"
@@ -204,6 +207,58 @@ struct MlirOptMainConfigCLOptions : public MlirOptMainConfig {
204207
cl::location(generateReproducerFileFlag), cl::init(""),
205208
cl::value_desc("filename"));
206209

210+
static cl::OptionCategory remarkCategory(
211+
"Remark Options",
212+
"Filter remarks by regular expression (llvm::Regex syntax).");
213+
214+
static llvm::cl::opt<RemarkFormat, /*ExternalStorage=*/true> remarkFormat{
215+
"remark-format",
216+
llvm::cl::desc("Specify the format for remark output."),
217+
cl::location(remarkFormatFlag),
218+
llvm::cl::value_desc("format"),
219+
llvm::cl::init(REMARK_FORMAT_STDOUT),
220+
llvm::cl::values(
221+
clEnumValN(REMARK_FORMAT_STDOUT, "emitRemark",
222+
"Print as emitRemark to command-line"),
223+
clEnumValN(REMARK_FORMAT_YAML, "yaml", "Print yaml file"),
224+
clEnumValN(REMARK_FORMAT_BITSTREAM, "bitstream",
225+
"Print bitstream file")),
226+
llvm::cl::cat(remarkCategory)};
227+
228+
static cl::opt<std::string, /*ExternalStorage=*/true> remarksAll(
229+
"remarks-filter",
230+
cl::desc("Show all remarks: passed, missed, failed, analysis"),
231+
cl::location(remarksAllFilterFlag), cl::init(""),
232+
cl::cat(remarkCategory));
233+
234+
static cl::opt<std::string, /*ExternalStorage=*/true> remarksFile(
235+
"remarks-output-file",
236+
cl::desc(
237+
"Output file for yaml and bitstream remark formats. Default is "
238+
"mlir-remarks.yaml or mlir-remarks.bitstream"),
239+
cl::location(remarksOutputFileFlag), cl::init(""),
240+
cl::cat(remarkCategory));
241+
242+
static cl::opt<std::string, /*ExternalStorage=*/true> remarksPassed(
243+
"remarks-filter-passed", cl::desc("Show passed remarks"),
244+
cl::location(remarksPassedFilterFlag), cl::init(""),
245+
cl::cat(remarkCategory));
246+
247+
static cl::opt<std::string, /*ExternalStorage=*/true> remarksFailed(
248+
"remarks-filter-failed", cl::desc("Show failed remarks"),
249+
cl::location(remarksFailedFilterFlag), cl::init(""),
250+
cl::cat(remarkCategory));
251+
252+
static cl::opt<std::string, /*ExternalStorage=*/true> remarksMissed(
253+
"remarks-filter-missed", cl::desc("Show missed remarks"),
254+
cl::location(remarksMissedFilterFlag), cl::init(""),
255+
cl::cat(remarkCategory));
256+
257+
static cl::opt<std::string, /*ExternalStorage=*/true> remarksAnalyse(
258+
"remarks-filter-analyse", cl::desc("Show analysis remarks"),
259+
cl::location(remarksAnalyseFilterFlag), cl::init(""),
260+
cl::cat(remarkCategory));
261+
207262
/// Set the callback to load a pass plugin.
208263
passPlugins.setCallback([&](const std::string &pluginPath) {
209264
auto plugin = PassPlugin::load(pluginPath);
@@ -241,23 +296,23 @@ class DiagnosticFilter : public ScopedDiagnosticHandler {
241296
setHandler([verbosityLevel, showNotes](Diagnostic &diag) {
242297
auto severity = diag.getSeverity();
243298
switch (severity) {
244-
case DiagnosticSeverity::Error:
299+
case mlir::DiagnosticSeverity::Error:
245300
// failure indicates that the error is not handled by the filter and
246301
// goes through to the default handler. Therefore, the error can be
247302
// successfully printed.
248303
return failure();
249-
case DiagnosticSeverity::Warning:
304+
case mlir::DiagnosticSeverity::Warning:
250305
if (verbosityLevel == VerbosityLevel::ErrorsOnly)
251306
return success();
252307
else
253308
return failure();
254-
case DiagnosticSeverity::Remark:
309+
case mlir::DiagnosticSeverity::Remark:
255310
if (verbosityLevel == VerbosityLevel::ErrorsOnly ||
256311
verbosityLevel == VerbosityLevel::ErrorsAndWarnings)
257312
return success();
258313
else
259314
return failure();
260-
case DiagnosticSeverity::Note:
315+
case mlir::DiagnosticSeverity::Note:
261316
if (showNotes)
262317
return failure();
263318
else
@@ -462,6 +517,41 @@ performActions(raw_ostream &os,
462517

463518
context->enableMultithreading(wasThreadingEnabled);
464519

520+
remark::RemarkCategories cats{
521+
config.getRemarksAllFilter(), config.getRemarksPassedFilter(),
522+
config.getRemarksMissedFilter(), config.getRemarksAnalyseFilter(),
523+
config.getRemarksFailedFilter()};
524+
525+
mlir::MLIRContext &ctx = *context;
526+
527+
switch (config.getRemarkFormat()) {
528+
case REMARK_FORMAT_STDOUT:
529+
if (failed(mlir::remark::enableOptimizationRemarks(
530+
ctx, nullptr, cats, true /*printAsEmitRemarks*/)))
531+
return failure();
532+
break;
533+
534+
case REMARK_FORMAT_YAML: {
535+
std::string file = config.getRemarksOutputFile().empty()
536+
? "mlir-remarks.yaml"
537+
: config.getRemarksOutputFile();
538+
if (failed(mlir::remark::enableOptimizationRemarksWithLLVMStreamer(
539+
ctx, file, llvm::remarks::Format::YAML, cats)))
540+
return failure();
541+
break;
542+
}
543+
544+
case REMARK_FORMAT_BITSTREAM: {
545+
std::string file = config.getRemarksOutputFile().empty()
546+
? "mlir-remarks.bitstream"
547+
: config.getRemarksOutputFile();
548+
if (failed(mlir::remark::enableOptimizationRemarksWithLLVMStreamer(
549+
ctx, file, llvm::remarks::Format::Bitstream, cats)))
550+
return failure();
551+
break;
552+
}
553+
}
554+
465555
// Prepare the pass manager, applying command-line and reproducer options.
466556
PassManager pm(op.get()->getName(), PassManager::Nesting::Implicit);
467557
pm.enableVerifier(config.shouldVerifyPasses());
@@ -523,8 +613,8 @@ processBuffer(raw_ostream &os, std::unique_ptr<MemoryBuffer> ownedBuffer,
523613
SMLoc());
524614
sourceMgr->AddNewSourceBuffer(std::move(ownedBuffer), SMLoc());
525615

526-
// Create a context just for the current buffer. Disable threading on creation
527-
// since we'll inject the thread-pool separately.
616+
// Create a context just for the current buffer. Disable threading on
617+
// creation since we'll inject the thread-pool separately.
528618
MLIRContext context(registry, MLIRContext::Threading::DISABLED);
529619
if (threadPool)
530620
context.setThreadPool(*threadPool);
@@ -669,9 +759,9 @@ LogicalResult mlir::MlirOptMain(int argc, char **argv,
669759
if (config.shouldListPasses())
670760
return printRegisteredPassesAndReturn();
671761

672-
// When reading from stdin and the input is a tty, it is often a user mistake
673-
// and the process "appears to be stuck". Print a message to let the user know
674-
// about it!
762+
// When reading from stdin and the input is a tty, it is often a user
763+
// mistake and the process "appears to be stuck". Print a message to let the
764+
// user know about it!
675765
if (inputFilename == "-" &&
676766
sys::Process::FileDescriptorIsDisplayed(fileno(stdin)))
677767
llvm::errs() << "(processing input from stdin now, hit ctrl-c/ctrl-d to "

mlir/test/Pass/remarks.mlir

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// RUN: mlir-opt %s --test-remark --remarks-filter-passed="category-1-passed" 2>&1 | FileCheck %s -check-prefix=CHECK-PASSED
2+
// RUN: mlir-opt %s --test-remark --remarks-filter-missed="a-category-1-missed" 2>&1 | FileCheck %s -check-prefix=CHECK-MISSED
3+
// RUN: mlir-opt %s --test-remark --remarks-filter-failed="category-2-failed" 2>&1 | FileCheck %s -check-prefix=CHECK-FAILED
4+
// RUN: mlir-opt %s --test-remark --remarks-filter-analyse="category-2-analysis" 2>&1 | FileCheck %s -check-prefix=CHECK-ANALYSIS
5+
// RUN: mlir-opt %s --test-remark --remarks-filter="category.*" 2>&1 | FileCheck %s -check-prefix=CHECK-ALL
6+
// RUN: mlir-opt %s --test-remark --remarks-filter="category-1.*" 2>&1 | FileCheck %s -check-prefix=CHECK-ALL1
7+
module @foo {
8+
"test.op"() : () -> ()
9+
10+
}
11+
12+
13+
// CHECK-PASSED: remarks.mlir:8:3: remark: [Passed] test-remark | Category:category-1-passed | Reason="because we are testing the remark pipeline", Remark="This is a test passed remark", Suggestion="try using the remark pipeline feature"
14+
// CHECK-MISSED:remarks.mlir:8:3: remark: [Missed] test-remark | Category:a-category-1-missed | Reason="because we are testing the remark pipeline", Remark="This is a test missed remark", Suggestion="try using the remark pipeline feature"
15+
// CHECK-FAILED: remarks.mlir:8:3: remark: [Failure] test-remark | Category:category-2-failed | Reason="because we are testing the remark pipeline", Remark="This is a test failed remark", Suggestion="try using the remark pipeline feature"
16+
// CHECK-ANALYSIS: remarks.mlir:8:3: remark: [Analysis] test-remark | Category:category-2-analysis | Remark="This is a test analysis remark"
17+
18+
19+
// CHECK-ALL: remarks.mlir:8:3: remark: [Passed] test-remark | Category:category-1-passed | Reason="because we are testing the remark pipeline", Remark="This is a test passed remark", Suggestion="try using the remark pipeline feature"
20+
// CHECK-ALL: remarks.mlir:8:3: remark: [Failure] test-remark | Category:category-2-failed | Reason="because we are testing the remark pipeline", Remark="This is a test failed remark", Suggestion="try using the remark pipeline feature"
21+
// CHECK-ALL: remarks.mlir:8:3: remark: [Analysis] test-remark | Category:category-2-analysis | Remark="This is a test analysis remark"
22+
23+
// CHECK-ALL1: remarks.mlir:8:3: remark: [Passed] test-remark | Category:category-1-passed | Reason="because we are testing the remark pipeline", Remark="This is a test passed remark", Suggestion="try using the remark pipeline feature"
24+
// CHECK-ALL1-NOT: remarks.mlir:8:3: remark: [Missed]
25+
// CHECK-ALL1-NOT: remarks.mlir:8:3: remark: [Failure]
26+
// CHECK-ALL1-NOT: remarks.mlir:8:3: remark: [Analysis]
27+
28+

mlir/test/lib/Pass/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ add_mlir_library(MLIRTestPass
44
TestConvertToSPIRVPass.cpp
55
TestDynamicPipeline.cpp
66
TestPassManager.cpp
7+
TestRemarksPass.cpp
78
TestSPIRVCPURunnerPipeline.cpp
89
TestVulkanRunnerPipeline.cpp
910

0 commit comments

Comments
 (0)