Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion mlir/include/mlir/IR/Remarks.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ namespace mlir::remark {
/// Define an the set of categories to accept. By default none are, the provided
/// regex matches against the category names for each kind of remark.
struct RemarkCategories {
std::optional<std::string> passed, missed, analysis, failed;
std::optional<std::string> all, passed, missed, analysis, failed;
};

/// Categories describe the outcome of an transformation, not the mechanics of
Expand Down
44 changes: 44 additions & 0 deletions mlir/include/mlir/Tools/mlir-opt/MlirOptMain.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ enum class VerbosityLevel {
ErrorsWarningsAndRemarks
};

using RemarkFormat = enum {
REMARK_FORMAT_STDOUT,
REMARK_FORMAT_YAML,
REMARK_FORMAT_BITSTREAM,
};

/// Configuration options for the mlir-opt tool.
/// This is intended to help building tools like mlir-opt by collecting the
/// supported options.
Expand Down Expand Up @@ -221,15 +227,53 @@ class MlirOptMainConfig {
}
bool shouldVerifyRoundtrip() const { return verifyRoundtripFlag; }

/// Checks if any remark filters are set.
bool shouldEmitRemarks() const {
// Emit all remarks only when no filters are specified.
const bool hasFilters =
!getRemarksAllFilter().empty() || !getRemarksPassedFilter().empty() ||
!getRemarksFailedFilter().empty() ||
!getRemarksMissedFilter().empty() || !getRemarksAnalyseFilter().empty();
return hasFilters;
}

/// Reproducer file generation (no crash required).
StringRef getReproducerFilename() const { return generateReproducerFileFlag; }

/// Set the reproducer output filename
RemarkFormat getRemarkFormat() const { return remarkFormatFlag; }
/// Set the remark format to use.
std::string getRemarksAllFilter() const { return remarksAllFilterFlag; }
/// Set the remark output file.
std::string getRemarksOutputFile() const { return remarksOutputFileFlag; }
/// Set the remark passed filters.
std::string getRemarksPassedFilter() const { return remarksPassedFilterFlag; }
/// Set the remark failed filters.
std::string getRemarksFailedFilter() const { return remarksFailedFilterFlag; }
/// Set the remark missed filters.
std::string getRemarksMissedFilter() const { return remarksMissedFilterFlag; }
/// Set the remark analyse filters.
std::string getRemarksAnalyseFilter() const {
return remarksAnalyseFilterFlag;
}

protected:
/// Allow operation with no registered dialects.
/// This option is for convenience during testing only and discouraged in
/// general.
bool allowUnregisteredDialectsFlag = false;

/// Remark format
RemarkFormat remarkFormatFlag;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sanitizer issue is because there is no default here.

/// Remark file to output to
std::string remarksOutputFileFlag = "";
/// Remark filters
std::string remarksAllFilterFlag = "";
std::string remarksPassedFilterFlag = "";
std::string remarksFailedFilterFlag = "";
std::string remarksMissedFilterFlag = "";
std::string remarksAnalyseFilterFlag = "";

/// Configuration for the debugging hooks.
tracing::DebugConfig debugConfig;

Expand Down
47 changes: 43 additions & 4 deletions mlir/lib/IR/Remarks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -248,17 +248,56 @@ RemarkEngine::initialize(std::unique_ptr<MLIRRemarkStreamerBase> streamer,
return success();
}

/// Returns true if filter is already anchored like ^...$
static bool isAnchored(llvm::StringRef s) {
s = s.trim();
return s.starts_with("^") && s.ends_with("$"); // note: startswith/endswith
}

/// Anchor the entire pattern so it matches the whole string.
static std::string anchorWhole(llvm::StringRef filter) {
if (isAnchored(filter))
return filter.str();
return (llvm::Twine("^(") + filter + ")$").str();
}

/// Build a combined filter from cats.all and a category-specific pattern.
/// If neither is present, return std::nullopt. Otherwise "(all|specific)"
/// and anchor once. Also validate before returning.
static std::optional<llvm::Regex>
buildFilter(const mlir::remark::RemarkCategories &cats,
const std::optional<std::string> &specific) {
llvm::SmallVector<llvm::StringRef, 2> parts;
if (cats.all && !cats.all->empty())
parts.emplace_back(*cats.all);
if (specific && !specific->empty())
parts.emplace_back(*specific);

if (parts.empty())
return std::nullopt;

std::string joined = llvm::join(parts, "|");
std::string anchored = anchorWhole(joined);

llvm::Regex rx(anchored);
std::string err;
if (!rx.isValid(err))
return std::nullopt;

return rx;
}

RemarkEngine::RemarkEngine(bool printAsEmitRemarks,
const RemarkCategories &cats)
: printAsEmitRemarks(printAsEmitRemarks) {
if (cats.passed)
passedFilter = llvm::Regex(cats.passed.value());
passedFilter = buildFilter(cats, cats.passed);
if (cats.missed)
missFilter = llvm::Regex(cats.missed.value());
missFilter = buildFilter(cats, cats.missed);
if (cats.analysis)
analysisFilter = llvm::Regex(cats.analysis.value());
analysisFilter = buildFilter(cats, cats.analysis);
if (cats.failed)
failedFilter = llvm::Regex(cats.failed.value());
failedFilter = buildFilter(cats, cats.failed);
}

llvm::LogicalResult mlir::remark::enableOptimizationRemarks(
Expand Down
1 change: 1 addition & 0 deletions mlir/lib/Tools/mlir-opt/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ add_mlir_library(MLIROptLib
MLIRPluginsLib
MLIRSupport
MLIRIRDL
MLIRRemarkStreamer
)
108 changes: 99 additions & 9 deletions mlir/lib/Tools/mlir-opt/MlirOptMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,19 @@
#include "mlir/IR/Diagnostics.h"
#include "mlir/IR/Location.h"
#include "mlir/IR/MLIRContext.h"
#include "mlir/IR/Remarks.h"
#include "mlir/Parser/Parser.h"
#include "mlir/Pass/PassManager.h"
#include "mlir/Pass/PassRegistry.h"
#include "mlir/Remark/RemarkStreamer.h"
#include "mlir/Support/FileUtilities.h"
#include "mlir/Support/Timing.h"
#include "mlir/Support/ToolUtilities.h"
#include "mlir/Tools/ParseUtilities.h"
#include "mlir/Tools/Plugins/DialectPlugin.h"
#include "mlir/Tools/Plugins/PassPlugin.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Remarks/RemarkFormat.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/LogicalResult.h"
Expand Down Expand Up @@ -204,6 +207,58 @@ struct MlirOptMainConfigCLOptions : public MlirOptMainConfig {
cl::location(generateReproducerFileFlag), cl::init(""),
cl::value_desc("filename"));

static cl::OptionCategory remarkCategory(
"Remark Options",
"Filter remarks by regular expression (llvm::Regex syntax).");

static llvm::cl::opt<RemarkFormat, /*ExternalStorage=*/true> remarkFormat{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a using namespace llvm and mlir above, so no need to qualify these here (and also inconsistent with ones just below and above).

"remark-format",
llvm::cl::desc("Specify the format for remark output."),
cl::location(remarkFormatFlag),
llvm::cl::value_desc("format"),
llvm::cl::init(REMARK_FORMAT_STDOUT),
llvm::cl::values(
clEnumValN(REMARK_FORMAT_STDOUT, "emitRemark",
"Print as emitRemark to command-line"),
clEnumValN(REMARK_FORMAT_YAML, "yaml", "Print yaml file"),
clEnumValN(REMARK_FORMAT_BITSTREAM, "bitstream",
"Print bitstream file")),
llvm::cl::cat(remarkCategory)};

static cl::opt<std::string, /*ExternalStorage=*/true> remarksAll(
"remarks-filter",
cl::desc("Show all remarks: passed, missed, failed, analysis"),
cl::location(remarksAllFilterFlag), cl::init(""),
cl::cat(remarkCategory));

static cl::opt<std::string, /*ExternalStorage=*/true> remarksFile(
"remarks-output-file",
cl::desc(
"Output file for yaml and bitstream remark formats. Default is "
"mlir-remarks.yaml or mlir-remarks.bitstream"),
cl::location(remarksOutputFileFlag), cl::init(""),
cl::cat(remarkCategory));

static cl::opt<std::string, /*ExternalStorage=*/true> remarksPassed(
"remarks-filter-passed", cl::desc("Show passed remarks"),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"remarks-filter-passed", cl::desc("Show passed remarks"),
"remarks-filter-passed", cl::desc("Regex to filter 'passed' remarks, matching the remark category name"),

(same for the others)

cl::location(remarksPassedFilterFlag), cl::init(""),
cl::cat(remarkCategory));

static cl::opt<std::string, /*ExternalStorage=*/true> remarksFailed(
"remarks-filter-failed", cl::desc("Show failed remarks"),
cl::location(remarksFailedFilterFlag), cl::init(""),
cl::cat(remarkCategory));

static cl::opt<std::string, /*ExternalStorage=*/true> remarksMissed(
"remarks-filter-missed", cl::desc("Show missed remarks"),
cl::location(remarksMissedFilterFlag), cl::init(""),
cl::cat(remarkCategory));

static cl::opt<std::string, /*ExternalStorage=*/true> remarksAnalyse(
"remarks-filter-analyse", cl::desc("Show analysis remarks"),
cl::location(remarksAnalyseFilterFlag), cl::init(""),
cl::cat(remarkCategory));

/// Set the callback to load a pass plugin.
passPlugins.setCallback([&](const std::string &pluginPath) {
auto plugin = PassPlugin::load(pluginPath);
Expand Down Expand Up @@ -241,23 +296,23 @@ class DiagnosticFilter : public ScopedDiagnosticHandler {
setHandler([verbosityLevel, showNotes](Diagnostic &diag) {
auto severity = diag.getSeverity();
switch (severity) {
case DiagnosticSeverity::Error:
case mlir::DiagnosticSeverity::Error:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why were these changes needed?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Conflict with the LLVM class of the same name I believe (there is an added include to LLVM)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, this is the reason exactly

// failure indicates that the error is not handled by the filter and
// goes through to the default handler. Therefore, the error can be
// successfully printed.
return failure();
case DiagnosticSeverity::Warning:
case mlir::DiagnosticSeverity::Warning:
if (verbosityLevel == VerbosityLevel::ErrorsOnly)
return success();
else
return failure();
case DiagnosticSeverity::Remark:
case mlir::DiagnosticSeverity::Remark:
if (verbosityLevel == VerbosityLevel::ErrorsOnly ||
verbosityLevel == VerbosityLevel::ErrorsAndWarnings)
return success();
else
return failure();
case DiagnosticSeverity::Note:
case mlir::DiagnosticSeverity::Note:
if (showNotes)
return failure();
else
Expand Down Expand Up @@ -462,6 +517,41 @@ performActions(raw_ostream &os,

context->enableMultithreading(wasThreadingEnabled);

remark::RemarkCategories cats{
config.getRemarksAllFilter(), config.getRemarksPassedFilter(),
config.getRemarksMissedFilter(), config.getRemarksAnalyseFilter(),
config.getRemarksFailedFilter()};

mlir::MLIRContext &ctx = *context;

switch (config.getRemarkFormat()) {
case REMARK_FORMAT_STDOUT:
if (failed(mlir::remark::enableOptimizationRemarks(
ctx, nullptr, cats, true /*printAsEmitRemarks*/)))
return failure();
break;

case REMARK_FORMAT_YAML: {
std::string file = config.getRemarksOutputFile().empty()
? "mlir-remarks.yaml"
: config.getRemarksOutputFile();
if (failed(mlir::remark::enableOptimizationRemarksWithLLVMStreamer(
ctx, file, llvm::remarks::Format::YAML, cats)))
return failure();
break;
}

case REMARK_FORMAT_BITSTREAM: {
std::string file = config.getRemarksOutputFile().empty()
? "mlir-remarks.bitstream"
: config.getRemarksOutputFile();
if (failed(mlir::remark::enableOptimizationRemarksWithLLVMStreamer(
ctx, file, llvm::remarks::Format::Bitstream, cats)))
return failure();
break;
}
}

// Prepare the pass manager, applying command-line and reproducer options.
PassManager pm(op.get()->getName(), PassManager::Nesting::Implicit);
pm.enableVerifier(config.shouldVerifyPasses());
Expand Down Expand Up @@ -523,8 +613,8 @@ processBuffer(raw_ostream &os, std::unique_ptr<MemoryBuffer> ownedBuffer,
SMLoc());
sourceMgr->AddNewSourceBuffer(std::move(ownedBuffer), SMLoc());

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

// When reading from stdin and the input is a tty, it is often a user mistake
// and the process "appears to be stuck". Print a message to let the user know
// about it!
// When reading from stdin and the input is a tty, it is often a user
// mistake and the process "appears to be stuck". Print a message to let the
// user know about it!
if (inputFilename == "-" &&
sys::Process::FileDescriptorIsDisplayed(fileno(stdin)))
llvm::errs() << "(processing input from stdin now, hit ctrl-c/ctrl-d to "
Expand Down
28 changes: 28 additions & 0 deletions mlir/test/Pass/remarks.mlir
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// RUN: mlir-opt %s --test-remark --remarks-filter-passed="category-1-passed" 2>&1 | FileCheck %s -check-prefix=CHECK-PASSED
// RUN: mlir-opt %s --test-remark --remarks-filter-missed="a-category-1-missed" 2>&1 | FileCheck %s -check-prefix=CHECK-MISSED
// RUN: mlir-opt %s --test-remark --remarks-filter-failed="category-2-failed" 2>&1 | FileCheck %s -check-prefix=CHECK-FAILED
// RUN: mlir-opt %s --test-remark --remarks-filter-analyse="category-2-analysis" 2>&1 | FileCheck %s -check-prefix=CHECK-ANALYSIS
// RUN: mlir-opt %s --test-remark --remarks-filter="category.*" 2>&1 | FileCheck %s -check-prefix=CHECK-ALL
// RUN: mlir-opt %s --test-remark --remarks-filter="category-1.*" 2>&1 | FileCheck %s -check-prefix=CHECK-ALL1
module @foo {
"test.op"() : () -> ()

}


// 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"
// 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"
// 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"
// CHECK-ANALYSIS: remarks.mlir:8:3: remark: [Analysis] test-remark | Category:category-2-analysis | Remark="This is a test analysis remark"


// 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"
// 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"
// CHECK-ALL: remarks.mlir:8:3: remark: [Analysis] test-remark | Category:category-2-analysis | Remark="This is a test analysis remark"

// 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"
// CHECK-ALL1-NOT: remarks.mlir:8:3: remark: [Missed]
// CHECK-ALL1-NOT: remarks.mlir:8:3: remark: [Failure]
// CHECK-ALL1-NOT: remarks.mlir:8:3: remark: [Analysis]


1 change: 1 addition & 0 deletions mlir/test/lib/Pass/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ add_mlir_library(MLIRTestPass
TestConvertToSPIRVPass.cpp
TestDynamicPipeline.cpp
TestPassManager.cpp
TestRemarksPass.cpp
TestSPIRVCPURunnerPipeline.cpp
TestVulkanRunnerPipeline.cpp

Expand Down
Loading