-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[MLIR] Implement remark emitting policies in MLIR #160526
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,6 +24,8 @@ | |
#include "mlir/IR/MLIRContext.h" | ||
#include "mlir/IR/Value.h" | ||
|
||
#include <functional> | ||
|
||
namespace mlir::remark { | ||
|
||
/// Define an the set of categories to accept. By default none are, the provided | ||
|
@@ -144,7 +146,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()) | ||
|
@@ -318,7 +320,7 @@ class InFlightRemark { | |
}; | ||
|
||
//===----------------------------------------------------------------------===// | ||
// MLIR Remark Streamer | ||
// Pluggable Remark Utilities | ||
//===----------------------------------------------------------------------===// | ||
|
||
/// Base class for MLIR remark streamers that is used to stream | ||
|
@@ -338,6 +340,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) | ||
//===----------------------------------------------------------------------===// | ||
|
@@ -355,6 +377,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; | ||
|
||
|
@@ -392,6 +416,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. | ||
|
@@ -407,8 +433,10 @@ 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); | ||
|
||
/// Report a remark. | ||
void report(const Remark &&remark); | ||
|
@@ -446,6 +474,54 @@ 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 { | ||
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; | ||
/// Optional user callback for intercepting postponed remarks. | ||
std::function<void(const detail::Remark &)> postponedRemarkCallback; | ||
|
||
public: | ||
RemarkEmittingPolicyFinal(); | ||
|
||
/// Register a callback to intercept postponed remarks before they are | ||
/// reported. The callback will be invoked for each postponed remark in | ||
/// finalize(). | ||
void | ||
setPostponedRemarkCallback(std::function<void(const detail::Remark &)> cb) { | ||
postponedRemarkCallback = std::move(cb); | ||
} | ||
|
||
void reportRemark(const detail::Remark &remark) override { | ||
postponedRemarks.erase(remark); | ||
postponedRemarks.insert(remark); | ||
} | ||
void finalize() override { | ||
for (auto &remark : postponedRemarks) { | ||
if (postponedRemarkCallback) | ||
postponedRemarkCallback(remark); | ||
reportImpl(remark); | ||
} | ||
} | ||
}; | ||
|
||
/// Create a Reason with llvm::formatv formatting. | ||
template <class... Ts> | ||
inline detail::LazyTextBuild reason(const char *fmt, Ts &&...ts) { | ||
|
@@ -505,16 +581,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>"; | ||
Comment on lines
+602
to
+603
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These string literals used as sentinel keys could conflict with actual remark names. Consider using more unique identifiers or a different approach like special enum values to avoid potential collisions. Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||
|
||
/// Helper to provide a static dummy context for sentinel keys. | ||
static mlir::MLIRContext *getStaticDummyContext() { | ||
static mlir::MLIRContext dummyContext; | ||
return &dummyContext; | ||
} | ||
|
||
/// 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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,7 +16,7 @@ | |
#include "llvm/ADT/StringRef.h" | ||
|
||
using namespace mlir::remark::detail; | ||
|
||
using namespace mlir::remark; | ||
//------------------------------------------------------------------------------ | ||
// Remark | ||
//------------------------------------------------------------------------------ | ||
|
@@ -70,7 +70,7 @@ static void printArgs(llvm::raw_ostream &os, llvm::ArrayRef<Remark::Arg> args) { | |
void Remark::print(llvm::raw_ostream &os, bool printLocation) const { | ||
// Header: [Type] pass:remarkName | ||
StringRef type = getRemarkTypeString(); | ||
StringRef categoryName = getFullCategoryName(); | ||
StringRef categoryName = getCombinedCategoryName(); | ||
StringRef name = remarkName; | ||
|
||
os << '[' << type << "] "; | ||
|
@@ -140,7 +140,7 @@ llvm::remarks::Remark Remark::generateRemark() const { | |
r.RemarkType = getRemarkType(); | ||
r.RemarkName = getRemarkName(); | ||
// MLIR does not use passes; instead, it has categories and sub-categories. | ||
r.PassName = getFullCategoryName(); | ||
r.PassName = getCombinedCategoryName(); | ||
r.FunctionName = getFunction(); | ||
r.Loc = locLambda(); | ||
for (const Remark::Arg &arg : getArgs()) { | ||
|
@@ -225,7 +225,7 @@ InFlightRemark RemarkEngine::emitOptimizationRemarkAnalysis(Location loc, | |
// RemarkEngine | ||
//===----------------------------------------------------------------------===// | ||
|
||
void RemarkEngine::report(const Remark &&remark) { | ||
void RemarkEngine::reportImpl(const Remark &remark) { | ||
// Stream the remark | ||
if (remarkStreamer) | ||
remarkStreamer->streamOptimizationRemark(remark); | ||
|
@@ -235,19 +235,19 @@ void RemarkEngine::report(const Remark &&remark) { | |
emitRemark(remark.getLocation(), remark.getMsg()); | ||
} | ||
|
||
void RemarkEngine::report(const Remark &&remark) { | ||
if (remarkEmittingPolicy) | ||
remarkEmittingPolicy->reportRemark(remark); | ||
} | ||
|
||
RemarkEngine::~RemarkEngine() { | ||
if (remarkEmittingPolicy) | ||
remarkEmittingPolicy->finalize(); | ||
|
||
if (remarkStreamer) | ||
remarkStreamer->finalize(); | ||
} | ||
|
||
llvm::LogicalResult | ||
RemarkEngine::initialize(std::unique_ptr<MLIRRemarkStreamerBase> streamer, | ||
std::string *errMsg) { | ||
// If you need to validate categories/filters, do so here and set errMsg. | ||
remarkStreamer = std::move(streamer); | ||
return success(); | ||
} | ||
|
||
/// Returns true if filter is already anchored like ^...$ | ||
static bool isAnchored(llvm::StringRef s) { | ||
s = s.trim(); | ||
|
@@ -300,19 +300,44 @@ RemarkEngine::RemarkEngine(bool printAsEmitRemarks, | |
failedFilter = buildFilter(cats, cats.failed); | ||
} | ||
|
||
llvm::LogicalResult RemarkEngine::initialize( | ||
std::unique_ptr<MLIRRemarkStreamerBase> streamer, | ||
std::unique_ptr<RemarkEmittingPolicyBase> remarkEmittingPolicy, | ||
std::string *errMsg) { | ||
|
||
remarkStreamer = std::move(streamer); | ||
|
||
// Capture `this`. Ensure RemarkEngine is not moved after this. | ||
auto reportFunc = [this](const Remark &r) { this->reportImpl(r); }; | ||
Comment on lines
+310
to
+311
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The comment warns that RemarkEngine must not be moved after this point, but there's no enforcement mechanism. Consider adding a moved flag or using other techniques to prevent accidental moves after initialization. Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you show a fix? |
||
remarkEmittingPolicy->initialize(ReportFn(std::move(reportFunc))); | ||
|
||
this->remarkEmittingPolicy = std::move(remarkEmittingPolicy); | ||
return success(); | ||
} | ||
|
||
llvm::LogicalResult mlir::remark::enableOptimizationRemarks( | ||
MLIRContext &ctx, | ||
std::unique_ptr<remark::detail::MLIRRemarkStreamerBase> streamer, | ||
const remark::RemarkCategories &cats, bool printAsEmitRemarks) { | ||
MLIRContext &ctx, std::unique_ptr<detail::MLIRRemarkStreamerBase> streamer, | ||
std::unique_ptr<detail::RemarkEmittingPolicyBase> remarkEmittingPolicy, | ||
const RemarkCategories &cats, bool printAsEmitRemarks) { | ||
auto engine = | ||
std::make_unique<remark::detail::RemarkEngine>(printAsEmitRemarks, cats); | ||
std::make_unique<detail::RemarkEngine>(printAsEmitRemarks, cats); | ||
|
||
std::string errMsg; | ||
if (failed(engine->initialize(std::move(streamer), &errMsg))) { | ||
if (failed(engine->initialize(std::move(streamer), | ||
std::move(remarkEmittingPolicy), &errMsg))) { | ||
llvm::report_fatal_error( | ||
llvm::Twine("Failed to initialize remark engine. Error: ") + errMsg); | ||
} | ||
ctx.setRemarkEngine(std::move(engine)); | ||
|
||
return success(); | ||
} | ||
|
||
//===----------------------------------------------------------------------===// | ||
// Remark emitting policies | ||
//===----------------------------------------------------------------------===// | ||
|
||
namespace mlir::remark { | ||
RemarkEmittingPolicyAll::RemarkEmittingPolicyAll() = default; | ||
RemarkEmittingPolicyFinal::RemarkEmittingPolicyFinal() = default; | ||
} // namespace mlir::remark |
Uh oh!
There was an error while loading. Please reload this page.