Skip to content
Open
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
140 changes: 133 additions & 7 deletions mlir/include/mlir/IR/Remarks.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
#include "llvm/Remarks/Remark.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/Regex.h"
#include <optional>

#include "mlir/IR/Diagnostics.h"
#include "mlir/IR/MLIRContext.h"
Expand Down Expand Up @@ -144,7 +143,7 @@ class Remark {

llvm::StringRef getCategoryName() const { return categoryName; }

llvm::StringRef getFullCategoryName() const {
llvm::StringRef getCombinedCategoryName() const {
if (categoryName.empty() && subCategoryName.empty())
return {};
if (subCategoryName.empty())
Expand Down Expand Up @@ -318,7 +317,7 @@ class InFlightRemark {
};

//===----------------------------------------------------------------------===//
// MLIR Remark Streamer
// Pluggable Remark Utilities
//===----------------------------------------------------------------------===//

/// Base class for MLIR remark streamers that is used to stream
Expand All @@ -338,6 +337,26 @@ class MLIRRemarkStreamerBase {
virtual void finalize() {} // optional
};

using ReportFn = llvm::unique_function<void(const Remark &)>;

/// Base class for MLIR remark emitting policies that is used to emit
/// optimization remarks to the underlying remark streamer. The derived classes
/// should implement the `reportRemark` method to provide the actual emitting
/// implementation.
class RemarkEmittingPolicyBase {
protected:
ReportFn reportImpl;

public:
RemarkEmittingPolicyBase() = default;
virtual ~RemarkEmittingPolicyBase() = default;

void initialize(ReportFn fn) { reportImpl = std::move(fn); }

virtual void reportRemark(const Remark &remark) = 0;
virtual void finalize() = 0;
};

//===----------------------------------------------------------------------===//
// Remark Engine (MLIR Context will own this class)
//===----------------------------------------------------------------------===//
Expand All @@ -355,6 +374,8 @@ class RemarkEngine {
std::optional<llvm::Regex> failedFilter;
/// The MLIR remark streamer that will be used to emit the remarks.
std::unique_ptr<MLIRRemarkStreamerBase> remarkStreamer;
/// The MLIR remark policy that will be used to emit the remarks.
std::unique_ptr<RemarkEmittingPolicyBase> remarkEmittingPolicy;
/// When is enabled, engine also prints remarks as mlir::emitRemarks.
bool printAsEmitRemarks = false;

Expand Down Expand Up @@ -392,6 +413,8 @@ class RemarkEngine {
InFlightRemark emitIfEnabled(Location loc, RemarkOpts opts,
bool (RemarkEngine::*isEnabled)(StringRef)
const);
/// Report a remark.
void reportImpl(const Remark &remark);

public:
/// Default constructor is deleted, use the other constructor.
Expand All @@ -407,8 +430,15 @@ class RemarkEngine {
~RemarkEngine();

/// Setup the remark engine with the given output path and format.
LogicalResult initialize(std::unique_ptr<MLIRRemarkStreamerBase> streamer,
std::string *errMsg);
LogicalResult
initialize(std::unique_ptr<MLIRRemarkStreamerBase> streamer,
std::unique_ptr<RemarkEmittingPolicyBase> remarkEmittingPolicy,
std::string *errMsg);

/// Get the remark emitting policy.
RemarkEmittingPolicyBase *getRemarkEmittingPolicy() const {
return remarkEmittingPolicy.get();
}

/// Report a remark.
void report(const Remark &&remark);
Expand Down Expand Up @@ -446,6 +476,46 @@ inline InFlightRemark withEngine(Fn fn, Location loc, Args &&...args) {

namespace mlir::remark {

//===----------------------------------------------------------------------===//
// Remark Emitting Policies
//===----------------------------------------------------------------------===//

/// Policy that emits all remarks.
class RemarkEmittingPolicyAll : public detail::RemarkEmittingPolicyBase {
public:
RemarkEmittingPolicyAll();

void reportRemark(const detail::Remark &remark) override {
assert(reportImpl && "reportImpl is not set");
reportImpl(remark);
}
void finalize() override {}
};

/// Policy that emits final remarks.
class RemarkEmittingPolicyFinal : public detail::RemarkEmittingPolicyBase {
private:
/// user can intercept them for custom processing via a registered callback,
/// otherwise they will be reported on engine destruction.
llvm::DenseSet<detail::Remark> postponedRemarks;

public:
RemarkEmittingPolicyFinal();

void reportRemark(const detail::Remark &remark) override {
postponedRemarks.erase(remark);
postponedRemarks.insert(remark);
}

void finalize() override {
assert(reportImpl && "reportImpl is not set");
for (auto &remark : postponedRemarks) {
if (reportImpl)
reportImpl(remark);
}
}
};

/// Create a Reason with llvm::formatv formatting.
template <class... Ts>
inline detail::LazyTextBuild reason(const char *fmt, Ts &&...ts) {
Expand Down Expand Up @@ -505,16 +575,72 @@ inline detail::InFlightRemark analysis(Location loc, RemarkOpts opts) {

/// Setup remarks for the context. This function will enable the remark engine
/// and set the streamer to be used for optimization remarks. The remark
/// categories are used to filter the remarks that will be emitted by the remark
/// engine. If a category is not specified, it will not be emitted. If
/// categories are used to filter the remarks that will be emitted by the
/// remark engine. If a category is not specified, it will not be emitted. If
/// `printAsEmitRemarks` is true, the remarks will be printed as
/// mlir::emitRemarks. 'streamer' must inherit from MLIRRemarkStreamerBase and
/// will be used to stream the remarks.
LogicalResult enableOptimizationRemarks(
MLIRContext &ctx,
std::unique_ptr<remark::detail::MLIRRemarkStreamerBase> streamer,
std::unique_ptr<remark::detail::RemarkEmittingPolicyBase>
remarkEmittingPolicy,
const remark::RemarkCategories &cats, bool printAsEmitRemarks = false);

} // namespace mlir::remark

// DenseMapInfo specialization for Remark
namespace llvm {
template <>
struct DenseMapInfo<mlir::remark::detail::Remark> {
static constexpr StringRef kEmptyKey = "<EMPTY_KEY>";
static constexpr StringRef kTombstoneKey = "<TOMBSTONE_KEY>";

/// Helper to provide a static dummy context for sentinel keys.
static mlir::MLIRContext *getStaticDummyContext() {
static mlir::MLIRContext dummyContext;
return &dummyContext;
}
Comment on lines +592 to +603
Copy link

Copilot AI Sep 29, 2025

Choose a reason for hiding this comment

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

[nitpick] The static MLIRContext in getStaticDummyContext() could lead to initialization order issues. Consider using a function-local static or lazy initialization pattern to ensure proper construction order.

Copilot uses AI. Check for mistakes.


/// Create an empty remark
static inline mlir::remark::detail::Remark getEmptyKey() {
return mlir::remark::detail::Remark(
mlir::remark::RemarkKind::RemarkUnknown, mlir::DiagnosticSeverity::Note,
mlir::UnknownLoc::get(getStaticDummyContext()),
mlir::remark::RemarkOpts::name(kEmptyKey));
}

/// Create a dead remark
static inline mlir::remark::detail::Remark getTombstoneKey() {
return mlir::remark::detail::Remark(
mlir::remark::RemarkKind::RemarkUnknown, mlir::DiagnosticSeverity::Note,
mlir::UnknownLoc::get(getStaticDummyContext()),
mlir::remark::RemarkOpts::name(kTombstoneKey));
}

/// Compute the hash value of the remark
static unsigned getHashValue(const mlir::remark::detail::Remark &remark) {
return llvm::hash_combine(
remark.getLocation().getAsOpaquePointer(),
llvm::hash_value(remark.getRemarkName()),
llvm::hash_value(remark.getCombinedCategoryName()));
}

static bool isEqual(const mlir::remark::detail::Remark &lhs,
const mlir::remark::detail::Remark &rhs) {
// Check for empty/tombstone keys first
if (lhs.getRemarkName() == kEmptyKey ||
lhs.getRemarkName() == kTombstoneKey ||
rhs.getRemarkName() == kEmptyKey ||
rhs.getRemarkName() == kTombstoneKey) {
return lhs.getRemarkName() == rhs.getRemarkName();
}

// For regular remarks, compare key identifying fields
return lhs.getLocation() == rhs.getLocation() &&
lhs.getRemarkName() == rhs.getRemarkName() &&
lhs.getCombinedCategoryName() == rhs.getCombinedCategoryName();
}
};
} // namespace llvm
#endif // MLIR_IR_REMARKS_H
1 change: 1 addition & 0 deletions mlir/include/mlir/Remark/RemarkStreamer.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ namespace mlir::remark {
/// mlir::emitRemarks.
LogicalResult enableOptimizationRemarksWithLLVMStreamer(
MLIRContext &ctx, StringRef filePath, llvm::remarks::Format fmt,
std::unique_ptr<detail::RemarkEmittingPolicyBase> remarkEmittingPolicy,
const RemarkCategories &cat, bool printAsEmitRemarks = false);

} // namespace mlir::remark
9 changes: 9 additions & 0 deletions mlir/include/mlir/Tools/mlir-opt/MlirOptMain.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ enum class RemarkFormat {
REMARK_FORMAT_BITSTREAM,
};

enum class RemarkPolicy {
REMARK_POLICY_ALL,
REMARK_POLICY_FINAL,
};

/// 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 @@ -242,6 +247,8 @@ class MlirOptMainConfig {

/// Set the reproducer output filename
RemarkFormat getRemarkFormat() const { return remarkFormatFlag; }
/// Set the remark policy to use.
RemarkPolicy getRemarkPolicy() const { return remarkPolicyFlag; }
/// Set the remark format to use.
std::string getRemarksAllFilter() const { return remarksAllFilterFlag; }
/// Set the remark output file.
Expand All @@ -265,6 +272,8 @@ class MlirOptMainConfig {

/// Remark format
RemarkFormat remarkFormatFlag = RemarkFormat::REMARK_FORMAT_STDOUT;
/// Remark policy
RemarkPolicy remarkPolicyFlag = RemarkPolicy::REMARK_POLICY_ALL;
/// Remark file to output to
std::string remarksOutputFileFlag = "";
/// Remark filters
Expand Down
15 changes: 9 additions & 6 deletions mlir/lib/IR/MLIRContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,11 @@ namespace mlir {
/// This class is completely private to this file, so everything is public.
class MLIRContextImpl {
public:
//===--------------------------------------------------------------------===//
// Remark
//===--------------------------------------------------------------------===//
std::unique_ptr<remark::detail::RemarkEngine> remarkEngine;

//===--------------------------------------------------------------------===//
// Debugging
//===--------------------------------------------------------------------===//
Expand All @@ -134,11 +139,6 @@ class MLIRContextImpl {
//===--------------------------------------------------------------------===//
DiagnosticEngine diagEngine;

//===--------------------------------------------------------------------===//
// Remark
//===--------------------------------------------------------------------===//
std::unique_ptr<remark::detail::RemarkEngine> remarkEngine;

//===--------------------------------------------------------------------===//
// Options
//===--------------------------------------------------------------------===//
Expand Down Expand Up @@ -357,7 +357,10 @@ MLIRContext::MLIRContext(const DialectRegistry &registry, Threading setting)
impl->affineUniquer.registerParametricStorageType<IntegerSetStorage>();
}

MLIRContext::~MLIRContext() = default;
MLIRContext::~MLIRContext() {
// finalize remark engine before destroying anything else.
impl->remarkEngine.reset();
}

/// Copy the specified array of elements into memory managed by the provided
/// bump pointer allocator. This assumes the elements are all PODs.
Expand Down
Loading
Loading