[Mlir-commits] [mlir] [MLIR] Introduce RemarkEngine + pluggable remark streaming (YAML/Bitstream) (PR #152474)
Guray Ozen
llvmlistbot at llvm.org
Fri Aug 15 06:04:16 PDT 2025
https://github.com/grypp updated https://github.com/llvm/llvm-project/pull/152474
>From e4292d11d2b249a894af4113f032e72884853647 Mon Sep 17 00:00:00 2001
From: Guray Ozen <gozen at nvidia.com>
Date: Thu, 7 Aug 2025 10:01:51 +0000
Subject: [PATCH 01/22] [MLIR] MLIR Integrate LLVM Optimization Remarks
Infrastructure
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This patch hooks LLVM’s remarks infrastructure into MLIR.
* Disabled by default, zero-cost when off.
Enable per context with `MLIRContext::setupOptimizationRemarks(...)`.
* YAML or bitstream output: via new `MLIRRemarkStreamer`
(subclasses `llvm::remarks::RemarkStreamer`).
* Implements simple APIs for passes:
User-code can directly prints remarks like:
```
reportOptimizationPass(loc, categoryName, "MyPass") << "vectorized";
reportOptimizationMiss(loc, categoryLoopUnroll, "MyPass",
"try increasing unroll factor") << "...";
reportOptimizationFail(loc, categoryLoopUnroll, "MyPass") << "...";
reportOptimizationAnalysis(loc, categoryLoopUnroll, "MyPass")
<< "estimated trip count: " << tripCount;
```
---
mlir/include/mlir/IR/MLIRContext.h | 15 +
mlir/include/mlir/IR/Remarks.h | 454 +++++++++++++++++++++++++++++
mlir/lib/IR/CMakeLists.txt | 6 +
mlir/lib/IR/MLIRContext.cpp | 35 +++
mlir/lib/IR/Remarks.cpp | 296 +++++++++++++++++++
mlir/unittests/IR/CMakeLists.txt | 1 +
mlir/unittests/IR/RemarkTest.cpp | 106 +++++++
7 files changed, 913 insertions(+)
create mode 100644 mlir/include/mlir/IR/Remarks.h
create mode 100644 mlir/lib/IR/Remarks.cpp
create mode 100644 mlir/unittests/IR/RemarkTest.cpp
diff --git a/mlir/include/mlir/IR/MLIRContext.h b/mlir/include/mlir/IR/MLIRContext.h
index ef8dab87f131a..ab8f4aed3fc79 100644
--- a/mlir/include/mlir/IR/MLIRContext.h
+++ b/mlir/include/mlir/IR/MLIRContext.h
@@ -32,6 +32,7 @@ class InFlightDiagnostic;
class Location;
class MLIRContextImpl;
class RegisteredOperationName;
+class RemarkEngine;
class StorageUniquer;
class IRUnit;
@@ -212,6 +213,9 @@ class MLIRContext {
/// Returns the diagnostic engine for this context.
DiagnosticEngine &getDiagEngine();
+ /// Returns the remark engine for this context.
+ std::unique_ptr<RemarkEngine> &getRemarkEngine();
+
/// Returns the storage uniquer used for creating affine constructs.
StorageUniquer &getAffineUniquer();
@@ -245,6 +249,14 @@ class MLIRContext {
/// (attributes, operations, types, etc.).
llvm::hash_code getRegistryHash();
+ /// Set up optimization remarks for the context.
+ void setupOptimizationRemarks(
+ StringRef outputPath, StringRef outputFormat, bool printAsEmitRemarks,
+ const std::optional<std::string> &categoryPassName = std::nullopt,
+ const std::optional<std::string> &categoryMissName = std::nullopt,
+ const std::optional<std::string> &categoryAnalysisName = std::nullopt,
+ const std::optional<std::string> &categoryFailedName = std::nullopt);
+
//===--------------------------------------------------------------------===//
// Action API
//===--------------------------------------------------------------------===//
@@ -281,6 +293,9 @@ class MLIRContext {
}
private:
+ /// Set the remark engine for this context.
+ void setRemarkEngine(std::unique_ptr<RemarkEngine> engine);
+
/// Return true if the given dialect is currently loading.
bool isDialectLoading(StringRef dialectNamespace);
diff --git a/mlir/include/mlir/IR/Remarks.h b/mlir/include/mlir/IR/Remarks.h
new file mode 100644
index 0000000000000..8f7e7a900e7f1
--- /dev/null
+++ b/mlir/include/mlir/IR/Remarks.h
@@ -0,0 +1,454 @@
+//===- Remarks.h - MLIR Optimization Remark ----------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines utilities for emitting optimization remarks.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef MLIR_IR_REMARKS_H
+#define MLIR_IR_REMARKS_H
+
+#include "llvm/IR/DiagnosticInfo.h"
+#include "llvm/IR/DiagnosticPrinter.h"
+#include "llvm/Remarks/Remark.h"
+#include "llvm/Remarks/RemarkStreamer.h"
+#include "llvm/Support/ToolOutputFile.h"
+
+#include "mlir/IR/Diagnostics.h"
+#include "mlir/IR/MLIRContext.h"
+
+namespace mlir {
+
+/// Defines different remark kinds that can be used to categorize remarks.
+enum class RemarkKind {
+ OptimizationRemarkUnknown = 0,
+ OptimizationRemarkPass,
+ OptimizationRemarkMissed,
+ OptimizationRemarkFailure,
+ OptimizationRemarkAnalysis,
+};
+
+//===----------------------------------------------------------------------===//
+// Remark Base Class
+//===----------------------------------------------------------------------===//
+class RemarkBase : public llvm::DiagnosticInfo {
+
+public:
+ RemarkBase(RemarkKind remarkKind, DiagnosticSeverity severity,
+ const char *passName, StringRef remarkName, Location loc,
+ std::optional<StringRef> functionName = std::nullopt)
+ : llvm::DiagnosticInfo(makeLLVMKind(remarkKind),
+ makeLLVMSeverity(severity)),
+ remarkKind(remarkKind), functionName(functionName), loc(loc),
+ passName(passName), remarkName(remarkName) {}
+
+ struct SetIsVerbose {};
+
+ struct SetExtraArgs {};
+
+ struct RemarkKeyValue {
+ std::string key;
+ std::string val;
+
+ explicit RemarkKeyValue(StringRef str = "") : key("String"), val(str) {}
+ RemarkKeyValue(StringRef key, Value value);
+ RemarkKeyValue(StringRef key, Type type);
+ RemarkKeyValue(StringRef key, StringRef s);
+ RemarkKeyValue(StringRef key, const char *s)
+ : RemarkKeyValue(key, StringRef(s)) {};
+ RemarkKeyValue(StringRef key, int n);
+ RemarkKeyValue(StringRef key, float n);
+ RemarkKeyValue(StringRef key, long n);
+ RemarkKeyValue(StringRef key, long long n);
+ RemarkKeyValue(StringRef key, unsigned n);
+ RemarkKeyValue(StringRef key, unsigned long n);
+ RemarkKeyValue(StringRef key, unsigned long long n);
+ RemarkKeyValue(StringRef key, bool b)
+ : key(key), val(b ? "true" : "false") {}
+ };
+
+ void insert(StringRef s);
+ void insert(RemarkKeyValue a);
+ void insert(SetIsVerbose v);
+ void insert(SetExtraArgs ea);
+
+ void print(llvm::DiagnosticPrinter &dp) const override;
+ void print() const;
+
+ virtual bool isEnabled() const = 0;
+ Location getLocation() const { return loc; }
+ llvm::remarks::RemarkLocation getRemarkLocation() const;
+ /// Diagnostic -> Remark
+ llvm::remarks::Remark generateRemark() const;
+
+ StringRef getFunction() const {
+ if (functionName)
+ return *functionName;
+ return "<unknown function>";
+ }
+ StringRef getPassName() const { return passName; }
+ StringRef getRemarkName() const { return remarkName; }
+ std::string getMsg() const;
+
+ bool isVerbose() const { return isVerboseRemark; }
+
+ ArrayRef<RemarkKeyValue> getArgs() const { return args; }
+
+ llvm::remarks::Type getRemarkType() const;
+
+protected:
+ /// Keeps the MLIR diagnostic kind, which is used to determine the
+ /// diagnostic kind in the LLVM remark streamer.
+ RemarkKind remarkKind;
+ /// Name of the convering function like interface
+ std::optional<std::string> functionName;
+
+ Location loc;
+ /// Name of the pass that triggers this report.
+ const char *passName;
+
+ /// Textual identifier for the remark (single-word, CamelCase). Can be used
+ /// by external tools reading the output file for optimization remarks to
+ /// identify the remark.
+ StringRef remarkName;
+
+ /// RemarkKeyValues collected via the streaming interface.
+ SmallVector<RemarkKeyValue, 4> args;
+
+ /// The remark is expected to be noisy.
+ bool isVerboseRemark = false;
+
+private:
+ /// Convert the MLIR diagnostic severity to LLVM diagnostic severity.
+ static llvm::DiagnosticSeverity
+ makeLLVMSeverity(DiagnosticSeverity severity) {
+ switch (severity) {
+ case DiagnosticSeverity::Note:
+ return llvm::DiagnosticSeverity::DS_Note;
+ case DiagnosticSeverity::Warning:
+ return llvm::DiagnosticSeverity::DS_Warning;
+ case DiagnosticSeverity::Error:
+ return llvm::DiagnosticSeverity::DS_Error;
+ case DiagnosticSeverity::Remark:
+ return llvm::DiagnosticSeverity::DS_Remark;
+ }
+ llvm_unreachable("Unknown diagnostic severity");
+ }
+ /// Convert the MLIR remark kind to LLVM diagnostic kind.
+ static llvm::DiagnosticKind makeLLVMKind(RemarkKind remarkKind) {
+ switch (remarkKind) {
+ case RemarkKind::OptimizationRemarkUnknown:
+ return llvm::DiagnosticKind::DK_Generic;
+ case RemarkKind::OptimizationRemarkPass:
+ return llvm::DiagnosticKind::DK_OptimizationRemark;
+ case RemarkKind::OptimizationRemarkMissed:
+ return llvm::DiagnosticKind::DK_OptimizationRemarkMissed;
+ case RemarkKind::OptimizationRemarkFailure:
+ return llvm::DiagnosticKind::DK_OptimizationFailure;
+ case RemarkKind::OptimizationRemarkAnalysis:
+ return llvm::DiagnosticKind::DK_OptimizationRemarkAnalysis;
+ }
+ llvm_unreachable("Unknown diagnostic kind");
+ }
+};
+
+// clang-format off
+template <class RemarkT>
+decltype(auto) operator<<(
+ RemarkT &&r,
+ std::enable_if_t<std::is_base_of_v<RemarkBase,
+ std::remove_reference_t<RemarkT>>,
+ StringRef>
+ s) {
+ r.insert(s);
+ return std::forward<RemarkT>(r);
+}
+
+template <class RemarkT>
+decltype(auto) operator<<(
+ RemarkT &&r,
+ std::enable_if_t<std::is_base_of_v<RemarkBase,
+ std::remove_reference_t<RemarkT>>,
+ RemarkBase::RemarkKeyValue>
+ a) {
+ r.insert(std::move(a));
+ return std::forward<RemarkT>(r);
+}
+
+template <class RemarkT>
+decltype(auto) operator<<(
+ RemarkT &&r,
+ std::enable_if_t<std::is_base_of_v<RemarkBase,
+ std::remove_reference_t<RemarkT>>,
+ RemarkBase::SetIsVerbose>
+ v) {
+ r.insert(v);
+ return std::forward<RemarkT>(r);
+}
+
+template <class RemarkT>
+decltype(auto) operator<<(
+ RemarkT &&r,
+ std::enable_if_t<std::is_base_of_v<RemarkBase,
+ std::remove_reference_t<RemarkT>>,
+ RemarkBase::SetExtraArgs>
+ ea) {
+ r.insert(ea);
+ return std::forward<RemarkT>(r);
+}
+// clang-format on
+
+//===----------------------------------------------------------------------===//
+// Shorthand aliases for different kinds of remarks.
+//===----------------------------------------------------------------------===//
+
+template <RemarkKind K, DiagnosticSeverity S>
+class OptRemarkBase final : public RemarkBase {
+public:
+ explicit OptRemarkBase(Location loc, StringRef passName, StringRef remarkName)
+ : RemarkBase(K, S, passName.data(), remarkName, loc) {}
+
+ bool isEnabled() const override { return true; }
+};
+
+using OptRemarkAnalysis = OptRemarkBase<RemarkKind::OptimizationRemarkAnalysis,
+ DiagnosticSeverity::Remark>;
+
+using OptRemarkPass = OptRemarkBase<RemarkKind::OptimizationRemarkPass,
+ DiagnosticSeverity::Remark>;
+
+using OptRemarkMissed = OptRemarkBase<RemarkKind::OptimizationRemarkMissed,
+ DiagnosticSeverity::Remark>;
+
+using OptRemarkFailure = OptRemarkBase<RemarkKind::OptimizationRemarkFailure,
+ DiagnosticSeverity::Remark>;
+
+class RemarkEngine;
+
+//===----------------------------------------------------------------------===//
+// InFlightRemark
+//===----------------------------------------------------------------------===//
+
+/// InFlightRemark is a RAII class that holds a reference to a RemarkBase
+/// instance and allows to build the remark using the << operator. The remark
+/// is emitted when the InFlightRemark instance is destroyed, which happens
+/// when the scope ends or when the InFlightRemark instance is moved.
+/// Similar to InFlightDiagnostic, but for remarks.
+class InFlightRemark {
+public:
+ explicit InFlightRemark(RemarkBase *diag) : remark(diag) {}
+ InFlightRemark(RemarkEngine &eng, RemarkBase *diag)
+ : owner(&eng), remark(diag) {}
+
+ InFlightRemark() = default; // empty ctor
+
+ template <typename T>
+ InFlightRemark &operator<<(T &&arg) {
+ if (remark)
+ *remark << std::forward<T>(arg);
+ return *this;
+ }
+
+ explicit operator bool() const { return remark != nullptr; }
+
+ ~InFlightRemark();
+
+ InFlightRemark(const InFlightRemark &) = delete;
+ InFlightRemark &operator=(const InFlightRemark &) = delete;
+ InFlightRemark(InFlightRemark &&) = default;
+ InFlightRemark &operator=(InFlightRemark &&) = default;
+
+private:
+ RemarkEngine *owner{nullptr};
+ std::unique_ptr<RemarkBase> remark;
+};
+
+//===----------------------------------------------------------------------===//
+// MLIR Remark Streamer
+//===----------------------------------------------------------------------===//
+
+class MLIRRemarkStreamer {
+ llvm::remarks::RemarkStreamer &remarkStreamer;
+
+public:
+ explicit MLIRRemarkStreamer(llvm::remarks::RemarkStreamer &remarkStreamer)
+ : remarkStreamer(remarkStreamer) {}
+
+ void streamOptimizationRemark(const RemarkBase &remark);
+};
+
+//===----------------------------------------------------------------------===//
+// Remark Engine (MLIR Context will own this class)
+//===----------------------------------------------------------------------===//
+
+class RemarkEngine {
+private:
+ /// The category for missed optimization remarks.
+ std::optional<llvm::Regex> missFilter;
+ /// The category for passed optimization remarks.
+ std::optional<llvm::Regex> passFilter;
+ /// The category for analysis remarks.
+ std::optional<llvm::Regex> analysisFilter;
+ /// The category for failed optimization remarks.
+ std::optional<llvm::Regex> failedFilter;
+ /// The output file for the remarks.
+ std::unique_ptr<llvm::ToolOutputFile> remarksFile;
+ /// The MLIR remark streamer that will be used to emit the remarks.
+ std::unique_ptr<MLIRRemarkStreamer> remarkStreamer;
+ std::unique_ptr<llvm::remarks::RemarkStreamer> llvmRemarkStreamer;
+ /// When is enabled, engine also prints remarks as mlir::emitRemarks.
+ bool printAsEmitRemarks;
+ /// The main MLIR remark streamer that will be used to emit the remarks.
+ MLIRRemarkStreamer *getLLVMRemarkStreamer() { return remarkStreamer.get(); }
+ const MLIRRemarkStreamer *getLLVMRemarkStreamer() const {
+ return remarkStreamer.get();
+ }
+ void
+ setLLVMRemarkStreamer(std::unique_ptr<MLIRRemarkStreamer> remarkStreamer) {
+ this->remarkStreamer = std::move(remarkStreamer);
+ }
+
+ /// Get the main MLIR remark streamer that will be used to emit the remarks.
+ llvm::remarks::RemarkStreamer *getMainRemarkStreamer() {
+ return llvmRemarkStreamer.get();
+ }
+ const llvm::remarks::RemarkStreamer *getMainRemarkStreamer() const {
+ return llvmRemarkStreamer.get();
+ }
+ /// Set the main remark streamer to be used by the engine.
+ void setMainRemarkStreamer(
+ std::unique_ptr<llvm::remarks::RemarkStreamer> mainRemarkStreamer) {
+ llvmRemarkStreamer = std::move(mainRemarkStreamer);
+ }
+
+ /// Return true if missed optimization remarks are enabled, override
+ /// to provide different implementation.
+ bool isMissedOptRemarkEnabled(StringRef categoryName) const;
+
+ /// Return true if passed optimization remarks are enabled, override
+ /// to provide different implementation.
+ bool isPassedOptRemarkEnabled(StringRef categoryName) const;
+
+ /// Return true if analysis optimization remarks are enabled, override
+ /// to provide different implementation.
+ bool isAnalysisOptRemarkEnabled(StringRef categoryName) const;
+
+ /// Return true if analysis optimization remarks are enabled, override
+ /// to provide different implementation.
+ bool isFailedOptRemarkEnabled(StringRef categoryName) const;
+
+ /// Return true if any type of remarks are enabled for this pass.
+ bool isAnyRemarkEnabled(StringRef categoryName) const {
+ return (isMissedOptRemarkEnabled(categoryName) ||
+ isPassedOptRemarkEnabled(categoryName) ||
+ isFailedOptRemarkEnabled(categoryName) ||
+ isAnalysisOptRemarkEnabled(categoryName));
+ }
+
+ /// Emit a remark using the given maker function, which should return
+ /// a RemarkBase instance. The remark will be emitted using the main
+ /// remark streamer.
+ template <typename RemarkT, typename... Args>
+ InFlightRemark makeRemark(Args &&...args);
+
+ template <typename RemarkT>
+ InFlightRemark
+ emitIfEnabled(Location loc, StringRef passName, StringRef category,
+ bool (RemarkEngine::*isEnabled)(StringRef) const);
+
+public:
+ RemarkEngine() = delete;
+ /// Constructs Remark engine with optional category names
+ RemarkEngine(bool printAsEmitRemarks,
+ std::optional<std::string> categoryPassName = std::nullopt,
+ std::optional<std::string> categoryMissName = std::nullopt,
+ std::optional<std::string> categoryAnalysisName = std::nullopt,
+ std::optional<std::string> categoryFailedName = std::nullopt);
+
+ /// Destructor that will close the output file and reset the
+ /// main remark streamer.
+ ~RemarkEngine();
+
+ /// Setup the remark engine with the given output path and format.
+ llvm::Error initialize(StringRef outputPath, StringRef outputFormat);
+
+ /// Report a diagnostic remark.
+ void report(const RemarkBase &&diag);
+
+ /// Report a successful remark, this will create an InFlightRemark
+ /// that can be used to build the remark using the << operator.
+ InFlightRemark emitOptimizationRemark(Location loc, StringRef passName,
+ StringRef category);
+ /// Report a missed optimization remark
+ /// that can be used to build the remark using the << operator.
+ InFlightRemark emitOptimizationRemarkMiss(Location loc, StringRef passName,
+ StringRef category);
+ /// Report a failed optimization remark, this will create an InFlightRemark
+ /// that can be used to build the remark using the << operator.
+ InFlightRemark emitOptimizationRemarkFailure(Location loc, StringRef passName,
+ StringRef category);
+ /// Report an analysis remark, this will create an InFlightRemark
+ /// that can be used to build the remark using the << operator.
+ InFlightRemark emitOptimizationRemarkAnalysis(Location loc,
+ StringRef passName,
+ StringRef category);
+};
+
+//===----------------------------------------------------------------------===//
+// Emitters
+//===----------------------------------------------------------------------===//
+
+using Suggestion = RemarkBase::RemarkKeyValue;
+inline Suggestion suggest(StringRef txt) { return {"Suggestion", txt}; }
+
+template <typename Fn, typename... Args>
+[[nodiscard]] inline InFlightRemark withEngine(Fn fn, Location loc,
+ Args &&...args) {
+ MLIRContext *ctx = loc->getContext();
+
+ auto &enginePtr = ctx->getRemarkEngine();
+
+ if (RemarkEngine *eng = enginePtr.get())
+ return (eng->*fn)(loc, std::forward<Args>(args)...);
+
+ return {};
+}
+
+/// Report an optimization remark that was passed.
+inline InFlightRemark reportOptimizationPass(Location loc, StringRef cat,
+ StringRef passName) {
+ return withEngine(&RemarkEngine::emitOptimizationRemark, loc, passName, cat);
+}
+
+/// Report an optimization remark that was missed.
+inline InFlightRemark reportOptimizationMiss(Location loc, StringRef cat,
+ StringRef passName,
+ StringRef suggestion) {
+ auto r =
+ withEngine(&RemarkEngine::emitOptimizationRemarkMiss, loc, passName, cat);
+ if (r)
+ r << suggest(suggestion);
+ return r;
+}
+/// Report an optimization failure remark.
+inline InFlightRemark reportOptimizationFail(Location loc, StringRef cat,
+ StringRef passName) {
+ return withEngine(&RemarkEngine::emitOptimizationRemarkFailure, loc, passName,
+ cat);
+}
+
+/// Report an optimization analysis remark.
+inline InFlightRemark reportOptimizationAnalysis(Location loc, StringRef cat,
+ StringRef passName) {
+ return withEngine(&RemarkEngine::emitOptimizationRemarkAnalysis, loc,
+ passName, cat);
+}
+
+} // namespace mlir
+
+#endif // MLIR_IR_REMARKS_H
\ No newline at end of file
diff --git a/mlir/lib/IR/CMakeLists.txt b/mlir/lib/IR/CMakeLists.txt
index 3ef69cea18f0a..438e486f2ad4c 100644
--- a/mlir/lib/IR/CMakeLists.txt
+++ b/mlir/lib/IR/CMakeLists.txt
@@ -33,6 +33,7 @@ add_mlir_library(MLIRIR
PatternMatch.cpp
Region.cpp
RegionKindInterface.cpp
+ Remarks.cpp
SymbolTable.cpp
TensorEncoding.cpp
Types.cpp
@@ -69,5 +70,10 @@ add_mlir_library(MLIRIR
LINK_LIBS PUBLIC
MLIRSupport
+
+ LINK_COMPONENTS
+ Remarks
+ Core
+ BitstreamReader
)
diff --git a/mlir/lib/IR/MLIRContext.cpp b/mlir/lib/IR/MLIRContext.cpp
index 2d5381d43f863..5a37de1481eb9 100644
--- a/mlir/lib/IR/MLIRContext.cpp
+++ b/mlir/lib/IR/MLIRContext.cpp
@@ -25,12 +25,14 @@
#include "mlir/IR/Location.h"
#include "mlir/IR/OpImplementation.h"
#include "mlir/IR/OperationSupport.h"
+#include "mlir/IR/Remarks.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Debug.h"
+#include "llvm/Support/Error.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/Mutex.h"
#include "llvm/Support/RWMutex.h"
@@ -133,6 +135,11 @@ class MLIRContextImpl {
//===--------------------------------------------------------------------===//
DiagnosticEngine diagEngine;
+ //===--------------------------------------------------------------------===//
+ // Remark
+ //===--------------------------------------------------------------------===//
+ std::unique_ptr<RemarkEngine> remarkEngine;
+
//===--------------------------------------------------------------------===//
// Options
//===--------------------------------------------------------------------===//
@@ -387,6 +394,34 @@ bool MLIRContext::hasActionHandler() { return (bool)getImpl().actionHandler; }
/// Returns the diagnostic engine for this context.
DiagnosticEngine &MLIRContext::getDiagEngine() { return getImpl().diagEngine; }
+//===----------------------------------------------------------------------===//
+// Remark Handlers
+//===----------------------------------------------------------------------===//
+
+/// Returns the remark engine for this context.
+void MLIRContext::setRemarkEngine(std::unique_ptr<RemarkEngine> engine) {
+ getImpl().remarkEngine = std::move(engine);
+}
+
+std::unique_ptr<RemarkEngine> &MLIRContext::getRemarkEngine() {
+ return getImpl().remarkEngine;
+}
+
+void MLIRContext::setupOptimizationRemarks(
+ StringRef outputPath, StringRef outputFormat, bool printAsEmitRemarks,
+ const std::optional<std::string> &categoryPassName,
+ const std::optional<std::string> &categoryMissName,
+ const std::optional<std::string> &categoryAnalysisName,
+ const std::optional<std::string> &categoryFailedName) {
+ auto engine = std::make_unique<RemarkEngine>(
+ printAsEmitRemarks, categoryPassName, categoryMissName,
+ categoryAnalysisName, categoryFailedName);
+ llvm::Error e = engine->initialize(outputPath, outputFormat);
+ if (e)
+ llvm::report_fatal_error("Failed to initialize remark engine");
+ setRemarkEngine(std::move(engine));
+}
+
//===----------------------------------------------------------------------===//
// Dialect and Operation Registration
//===----------------------------------------------------------------------===//
diff --git a/mlir/lib/IR/Remarks.cpp b/mlir/lib/IR/Remarks.cpp
new file mode 100644
index 0000000000000..4d2aa95236f68
--- /dev/null
+++ b/mlir/lib/IR/Remarks.cpp
@@ -0,0 +1,296 @@
+//===- Remarks.cpp - MLIR Remarks ---------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "mlir/IR/Remarks.h"
+
+#include "mlir/IR/Attributes.h"
+#include "mlir/IR/BuiltinAttributes.h"
+#include "mlir/IR/Diagnostics.h"
+#include "mlir/IR/Value.h"
+
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/IR/LLVMRemarkStreamer.h"
+#include "llvm/Remarks/RemarkFormat.h"
+#include "llvm/Support/FileSystem.h"
+
+using namespace mlir;
+
+//------------------------------------------------------------------------------
+// RemarkBase
+//------------------------------------------------------------------------------
+
+RemarkBase::RemarkKeyValue::RemarkKeyValue(StringRef key, Value value)
+ : key(std::string(key)) {
+
+ llvm::raw_string_ostream rss(val);
+ rss << value;
+}
+
+RemarkBase::RemarkKeyValue::RemarkKeyValue(StringRef key, Type type)
+ : key(std::string(key)) {
+ llvm::raw_string_ostream os(val);
+ os << type;
+}
+
+RemarkBase::RemarkKeyValue::RemarkKeyValue(StringRef key, StringRef s)
+ : key(std::string(key)), val(s.str()) {}
+
+RemarkBase::RemarkKeyValue::RemarkKeyValue(StringRef key, int n)
+ : key(std::string(key)), val(llvm::itostr(n)) {}
+
+RemarkBase::RemarkKeyValue::RemarkKeyValue(StringRef key, float n)
+ : key(std::string(key)), val(std::to_string(n)) {}
+
+RemarkBase::RemarkKeyValue::RemarkKeyValue(StringRef key, long n)
+ : key(std::string(key)), val(llvm::itostr(n)) {}
+
+RemarkBase::RemarkKeyValue::RemarkKeyValue(StringRef key, long long n)
+ : key(std::string(key)), val(llvm::itostr(n)) {}
+
+RemarkBase::RemarkKeyValue::RemarkKeyValue(StringRef key, unsigned n)
+ : key(std::string(key)), val(llvm::utostr(n)) {}
+
+RemarkBase::RemarkKeyValue::RemarkKeyValue(StringRef key, unsigned long n)
+ : key(std::string(key)), val(llvm::utostr(n)) {}
+
+RemarkBase::RemarkKeyValue::RemarkKeyValue(StringRef key, unsigned long long n)
+ : key(std::string(key)), val(llvm::utostr(n)) {}
+
+void RemarkBase::print(llvm::DiagnosticPrinter &dp) const {
+ std::string str;
+ llvm::raw_string_ostream os(str);
+ getLocation()->print(os);
+ os.flush();
+ dp << str << ": " << getMsg();
+}
+
+void RemarkBase::print() const { emitError(getLocation(), getMsg()); }
+
+void RemarkBase::insert(StringRef s) { args.emplace_back(s); }
+
+void RemarkBase::insert(RemarkKeyValue a) { args.push_back(std::move(a)); }
+
+void RemarkBase::insert(SetIsVerbose v) { isVerboseRemark = true; }
+
+std::string RemarkBase::getMsg() const {
+ std::string str;
+ llvm::raw_string_ostream rss(str);
+ for (const RemarkBase::RemarkKeyValue &arg :
+ llvm::make_range(args.begin(), args.end()))
+ rss << arg.val;
+ return str;
+}
+
+llvm::remarks::Type RemarkBase::getRemarkType() const {
+ switch (remarkKind) {
+ case RemarkKind::OptimizationRemarkUnknown:
+ return llvm::remarks::Type::Unknown;
+ case RemarkKind::OptimizationRemarkPass:
+ return llvm::remarks::Type::Passed;
+ case RemarkKind::OptimizationRemarkMissed:
+ return llvm::remarks::Type::Missed;
+ case RemarkKind::OptimizationRemarkFailure:
+ return llvm::remarks::Type::Failure;
+ case RemarkKind::OptimizationRemarkAnalysis:
+ return llvm::remarks::Type::Analysis;
+ }
+ llvm_unreachable("Unknown remark kind");
+}
+
+llvm::remarks::RemarkLocation RemarkBase::getRemarkLocation() const {
+ if (auto flc = dyn_cast<FileLineColLoc>(getLocation())) {
+ auto *buf = new std::string("./" + flc.getFilename().str());
+ return {*buf, flc.getLine(), flc.getColumn()};
+ }
+ return {"<unknown file>", 0, 0};
+}
+
+llvm::remarks::Remark RemarkBase::generateRemark() const {
+ llvm::remarks::Remark r; // The result.
+ r.RemarkType = getRemarkType();
+ r.PassName = getPassName();
+ r.RemarkName = getRemarkName();
+ r.FunctionName = getFunction();
+ r.Loc = getRemarkLocation();
+ for (const RemarkBase::RemarkKeyValue &arg : getArgs()) {
+ r.Args.emplace_back();
+ r.Args.back().Key = arg.key;
+ r.Args.back().Val = arg.val;
+ }
+ return r;
+}
+
+//===----------------------------------------------------------------------===//
+// InFlightRemark
+//===----------------------------------------------------------------------===//
+
+InFlightRemark::~InFlightRemark() {
+ if (remark)
+ if (owner) {
+ owner->report(std::move(*remark));
+ }
+ owner = nullptr;
+}
+
+//===----------------------------------------------------------------------===//
+// Remark Engine
+//===----------------------------------------------------------------------===//
+
+template <typename RemarkT, typename... Args>
+InFlightRemark RemarkEngine::makeRemark(Args &&...args) {
+ return InFlightRemark(*this, new RemarkT(std::forward<Args>(args)...));
+}
+
+template <typename RemarkT>
+InFlightRemark
+RemarkEngine::emitIfEnabled(Location loc, StringRef passName,
+ StringRef category,
+ bool (RemarkEngine::*isEnabled)(StringRef) const) {
+ return (this->*isEnabled)(category)
+ ? makeRemark<RemarkT>(loc, category, passName)
+ : InFlightRemark{};
+}
+
+bool RemarkEngine::isMissedOptRemarkEnabled(StringRef categoryName) const {
+ return missFilter && missFilter->match(categoryName);
+}
+
+bool RemarkEngine::isPassedOptRemarkEnabled(StringRef categoryName) const {
+ return passFilter && passFilter->match(categoryName);
+}
+
+bool RemarkEngine::isAnalysisOptRemarkEnabled(StringRef categoryName) const {
+ return analysisFilter && analysisFilter->match(categoryName);
+}
+
+bool RemarkEngine::isFailedOptRemarkEnabled(StringRef categoryName) const {
+ return failedFilter && failedFilter->match(categoryName);
+}
+
+InFlightRemark RemarkEngine::emitOptimizationRemark(Location loc,
+ StringRef passName,
+ StringRef category) {
+ return emitIfEnabled<OptRemarkPass>(loc, passName, category,
+ &RemarkEngine::isPassedOptRemarkEnabled);
+}
+
+InFlightRemark RemarkEngine::emitOptimizationRemarkMiss(Location loc,
+ StringRef passName,
+ StringRef category) {
+ return emitIfEnabled<OptRemarkMissed>(
+ loc, passName, category, &RemarkEngine::isMissedOptRemarkEnabled);
+}
+
+InFlightRemark RemarkEngine::emitOptimizationRemarkFailure(Location loc,
+ StringRef passName,
+ StringRef category) {
+ return emitIfEnabled<OptRemarkFailure>(
+ loc, passName, category, &RemarkEngine::isFailedOptRemarkEnabled);
+}
+
+InFlightRemark
+RemarkEngine::emitOptimizationRemarkAnalysis(Location loc, StringRef passName,
+ StringRef category) {
+ return emitIfEnabled<OptRemarkAnalysis>(
+ loc, passName, category, &RemarkEngine::isAnalysisOptRemarkEnabled);
+}
+
+//===----------------------------------------------------------------------===//
+// RemarkStreamer
+//===----------------------------------------------------------------------===//
+void MLIRRemarkStreamer::streamOptimizationRemark(const RemarkBase &remark) {
+ if (!remarkStreamer.matchesFilter(remark.getPassName()))
+ return;
+
+ // First, convert the diagnostic to a remark.
+ llvm::remarks::Remark r = remark.generateRemark();
+ // Then, emit the remark through the serializer.
+ remarkStreamer.getSerializer().emit(r);
+}
+//===----------------------------------------------------------------------===//
+// Remarkengine
+//===----------------------------------------------------------------------===//
+
+void RemarkEngine::report(const RemarkBase &&diag) {
+ // Stream the remark
+ if (getLLVMRemarkStreamer() && remarksFile)
+ getLLVMRemarkStreamer()->streamOptimizationRemark(diag);
+
+ // Print using MLIR's diagnostic
+ if (printAsEmitRemarks) {
+ emitRemark(diag.getLocation(), diag.getMsg());
+ }
+}
+
+RemarkEngine::~RemarkEngine() {
+ if (remarksFile) {
+ remarksFile->keep();
+ remarksFile.reset();
+ }
+ setMainRemarkStreamer(nullptr);
+ setLLVMRemarkStreamer(nullptr);
+}
+
+llvm::Error RemarkEngine::initialize(StringRef outputPath,
+ StringRef outputFormat) {
+ if (remarksFile) {
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "RemarkEngine is already initialized with an output file");
+ }
+
+ auto fFormatOrErr = llvm::remarks::parseFormat(outputFormat);
+ if (!fFormatOrErr) {
+ return fFormatOrErr.takeError();
+ }
+ llvm::remarks::Format fFormat = *fFormatOrErr;
+ std::error_code errCode;
+ llvm::sys::fs::OpenFlags fFlags = (fFormat == llvm::remarks::Format::YAML)
+ ? llvm::sys::fs::OF_Text
+ : llvm::sys::fs::OF_None;
+
+ remarksFile =
+ std::make_unique<llvm::ToolOutputFile>(outputPath, errCode, fFlags);
+ if (errCode) {
+ remarksFile.reset(); // force an empty optional
+ return llvm::createStringError(errCode, "Failed to open remarks file");
+ }
+
+ auto remarkSerializer = llvm::remarks::createRemarkSerializer(
+ *fFormatOrErr, llvm::remarks::SerializerMode::Separate,
+ remarksFile->os());
+ if (llvm::Error err = remarkSerializer.takeError()) {
+ return err;
+ }
+
+ // Create the main remark streamer.
+ setMainRemarkStreamer(std::make_unique<llvm::remarks::RemarkStreamer>(
+ std::move(*remarkSerializer), outputFormat));
+
+ setLLVMRemarkStreamer(
+ std::make_unique<MLIRRemarkStreamer>(*getMainRemarkStreamer()));
+
+ return llvm::ErrorSuccess();
+}
+
+RemarkEngine::RemarkEngine(bool printAsEmitRemarks,
+ std::optional<std::string> categoryPassName,
+ std::optional<std::string> categoryMissName,
+ std::optional<std::string> categoryAnalysisName,
+ std::optional<std::string> categoryFailedName)
+ : printAsEmitRemarks(printAsEmitRemarks) {
+ if (categoryPassName)
+ passFilter = llvm::Regex(categoryPassName.value());
+ if (categoryMissName)
+ missFilter = llvm::Regex(categoryMissName.value());
+ if (categoryAnalysisName)
+ analysisFilter = llvm::Regex(categoryAnalysisName.value());
+ if (categoryFailedName)
+ failedFilter = llvm::Regex(categoryFailedName.value());
+}
diff --git a/mlir/unittests/IR/CMakeLists.txt b/mlir/unittests/IR/CMakeLists.txt
index a46e64718dab9..07b242f7440bb 100644
--- a/mlir/unittests/IR/CMakeLists.txt
+++ b/mlir/unittests/IR/CMakeLists.txt
@@ -14,6 +14,7 @@ add_mlir_unittest(MLIRIRTests
MemrefLayoutTest.cpp
OperationSupportTest.cpp
PatternMatchTest.cpp
+ RemarkTest.cpp
ShapedTypeTest.cpp
SymbolTableTest.cpp
TypeTest.cpp
diff --git a/mlir/unittests/IR/RemarkTest.cpp b/mlir/unittests/IR/RemarkTest.cpp
new file mode 100644
index 0000000000000..8abddb44fd428
--- /dev/null
+++ b/mlir/unittests/IR/RemarkTest.cpp
@@ -0,0 +1,106 @@
+//===- RemarkTest.cpp - Remark unit tests -------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "mlir/IR/Diagnostics.h"
+#include "mlir/IR/Remarks.h"
+#include "mlir/Support/TypeID.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/YAMLParser.h"
+#include "gtest/gtest.h"
+#include <optional>
+
+using namespace llvm;
+using namespace mlir;
+using namespace mlir::detail;
+
+namespace {
+
+TEST(Remark, TestOutputOptimizationRemark) {
+ const auto *pass1Msg = "My message";
+ const auto *pass2Msg = "My another message";
+ const auto *pass3Msg = "Do not show this message";
+
+ auto *context = new MLIRContext();
+
+ std::string categoryLoopunroll("LoopUnroll");
+ std::string categoryInline("Inliner");
+ std::string myPassname1("myPass1");
+ std::string myPassname2("myPass2");
+ std::string funcName("myFunc");
+ std::string yamlFile = "/tmp/remarks.yaml";
+
+ Location loc = UnknownLoc::get(context);
+
+ // Setup the remark engine
+ context->setupOptimizationRemarks(yamlFile, "yaml", true, categoryLoopunroll,
+ std::nullopt, std::nullopt,
+ categoryLoopunroll);
+
+ // Remark 1: pass, category LoopUnroll
+ reportOptimizationPass(loc, categoryLoopunroll, myPassname1) << pass1Msg;
+ // Remark 2: failure, category LoopUnroll
+ reportOptimizationFail(loc, categoryLoopunroll, myPassname2) << pass2Msg;
+ // Remark 3: pass, category Inline (should not be printed)
+ reportOptimizationPass(loc, categoryInline, myPassname1) << pass3Msg;
+
+ delete context;
+
+ // Read the file
+ auto bufferOrErr = MemoryBuffer::getFile(yamlFile);
+ ASSERT_TRUE(static_cast<bool>(bufferOrErr)) << "Failed to open remarks file";
+ std::string content = bufferOrErr.get()->getBuffer().str();
+
+ // Remark 1: pass, should be printed
+ EXPECT_NE(content.find("--- !Passed"), std::string::npos);
+ EXPECT_NE(content.find("Pass: " + categoryLoopunroll),
+ std::string::npos);
+ EXPECT_NE(content.find("Name: " + myPassname1), std::string::npos);
+ EXPECT_NE(content.find("String: " + std::string(pass1Msg)),
+ std::string::npos);
+
+ // Remark 2: failure, should be printed
+ EXPECT_NE(content.find("--- !Failure"), std::string::npos);
+ EXPECT_NE(content.find("Name: " + myPassname2), std::string::npos);
+ EXPECT_NE(content.find("String: " + std::string(pass2Msg)),
+ std::string::npos);
+
+ // Remark 3: pass, category Inline (should not be printed)
+ EXPECT_EQ(content.find("String: " + std::string(pass3Msg)),
+ std::string::npos);
+}
+
+TEST(Remark, TestNoOutputOptimizationRemark) {
+ const auto *pass1Msg = "My message";
+
+ auto *context = new MLIRContext();
+
+ std::string categoryFailName("myImportantCategory");
+ std::string myPassname1("myPass1");
+ std::string funcName("myFunc");
+ std::string yamlFile = "/tmp/remarks.yaml";
+
+ std::error_code ec =
+ llvm::sys::fs::remove(yamlFile, /*IgnoreNonExisting=*/true);
+ if (ec) {
+ FAIL() << "Failed to remove file " << yamlFile << ": " << ec.message();
+ }
+
+ Location loc = UnknownLoc::get(context);
+ reportOptimizationFail(loc, categoryFailName, myPassname1) << pass1Msg;
+
+ delete context;
+
+ // No setup, so no output file should be created
+ // check!
+ bool fileExists = llvm::sys::fs::exists(yamlFile);
+ EXPECT_FALSE(fileExists)
+ << "Expected no YAML file to be created without setupOptimizationRemarks";
+}
+
+} // namespace
>From 066fe4730af59119d1d78853e6c7f31c3319e5ff Mon Sep 17 00:00:00 2001
From: Guray Ozen <gozen at nvidia.com>
Date: Thu, 7 Aug 2025 11:19:47 +0000
Subject: [PATCH 02/22] address copilot
---
mlir/include/mlir/IR/Remarks.h | 1 -
mlir/lib/IR/Remarks.cpp | 16 +++++++---------
mlir/unittests/IR/RemarkTest.cpp | 17 ++++++++++++-----
3 files changed, 19 insertions(+), 15 deletions(-)
diff --git a/mlir/include/mlir/IR/Remarks.h b/mlir/include/mlir/IR/Remarks.h
index 8f7e7a900e7f1..240857b388587 100644
--- a/mlir/include/mlir/IR/Remarks.h
+++ b/mlir/include/mlir/IR/Remarks.h
@@ -82,7 +82,6 @@ class RemarkBase : public llvm::DiagnosticInfo {
virtual bool isEnabled() const = 0;
Location getLocation() const { return loc; }
- llvm::remarks::RemarkLocation getRemarkLocation() const;
/// Diagnostic -> Remark
llvm::remarks::Remark generateRemark() const;
diff --git a/mlir/lib/IR/Remarks.cpp b/mlir/lib/IR/Remarks.cpp
index 4d2aa95236f68..c0bae3915f0e0 100644
--- a/mlir/lib/IR/Remarks.cpp
+++ b/mlir/lib/IR/Remarks.cpp
@@ -103,21 +103,19 @@ llvm::remarks::Type RemarkBase::getRemarkType() const {
llvm_unreachable("Unknown remark kind");
}
-llvm::remarks::RemarkLocation RemarkBase::getRemarkLocation() const {
- if (auto flc = dyn_cast<FileLineColLoc>(getLocation())) {
- auto *buf = new std::string("./" + flc.getFilename().str());
- return {*buf, flc.getLine(), flc.getColumn()};
- }
- return {"<unknown file>", 0, 0};
-}
-
llvm::remarks::Remark RemarkBase::generateRemark() const {
+ auto locLambda = [&]() -> llvm::remarks::RemarkLocation {
+ if (auto flc = dyn_cast<FileLineColLoc>(getLocation()))
+ return {flc.getFilename(), flc.getLine(), flc.getColumn()};
+ return {"<unknown file>", 0, 0};
+ };
+
llvm::remarks::Remark r; // The result.
r.RemarkType = getRemarkType();
r.PassName = getPassName();
r.RemarkName = getRemarkName();
r.FunctionName = getFunction();
- r.Loc = getRemarkLocation();
+ r.Loc = locLambda();
for (const RemarkBase::RemarkKeyValue &arg : getArgs()) {
r.Args.emplace_back();
r.Args.back().Key = arg.key;
diff --git a/mlir/unittests/IR/RemarkTest.cpp b/mlir/unittests/IR/RemarkTest.cpp
index 8abddb44fd428..91bb6d82a6920 100644
--- a/mlir/unittests/IR/RemarkTest.cpp
+++ b/mlir/unittests/IR/RemarkTest.cpp
@@ -33,8 +33,12 @@ TEST(Remark, TestOutputOptimizationRemark) {
std::string myPassname1("myPass1");
std::string myPassname2("myPass2");
std::string funcName("myFunc");
- std::string yamlFile = "/tmp/remarks.yaml";
-
+ SmallString<64> tmpPathStorage;
+ sys::fs::createUniquePath("remarks-%%%%%%.yaml", tmpPathStorage,
+ /*MakeAbsolute=*/true);
+ std::string yamlFile =
+ std::string(tmpPathStorage.data(), tmpPathStorage.size());
+ ASSERT_FALSE(yamlFile.empty());
Location loc = UnknownLoc::get(context);
// Setup the remark engine
@@ -77,14 +81,17 @@ TEST(Remark, TestOutputOptimizationRemark) {
TEST(Remark, TestNoOutputOptimizationRemark) {
const auto *pass1Msg = "My message";
-
auto *context = new MLIRContext();
std::string categoryFailName("myImportantCategory");
std::string myPassname1("myPass1");
std::string funcName("myFunc");
- std::string yamlFile = "/tmp/remarks.yaml";
-
+ SmallString<64> tmpPathStorage;
+ sys::fs::createUniquePath("remarks-%%%%%%.yaml", tmpPathStorage,
+ /*MakeAbsolute=*/true);
+ std::string yamlFile =
+ std::string(tmpPathStorage.data(), tmpPathStorage.size());
+ ASSERT_FALSE(yamlFile.empty());
std::error_code ec =
llvm::sys::fs::remove(yamlFile, /*IgnoreNonExisting=*/true);
if (ec) {
>From fa75d31ae90429b73a0701f820f8a165b85a7ba9 Mon Sep 17 00:00:00 2001
From: Guray Ozen <gozen at nvidia.com>
Date: Fri, 8 Aug 2025 12:37:27 +0000
Subject: [PATCH 03/22] add doc
---
mlir/docs/Remarks.md | 91 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 91 insertions(+)
create mode 100644 mlir/docs/Remarks.md
diff --git a/mlir/docs/Remarks.md b/mlir/docs/Remarks.md
new file mode 100644
index 0000000000000..4885f66d00d05
--- /dev/null
+++ b/mlir/docs/Remarks.md
@@ -0,0 +1,91 @@
+# Remark Infrastructure
+
+[TOC]
+
+Optimization remarks are structured, machine- and human-readable notes emitted by passes to explain what was optimized, what was missed, and why. MLIR integrates LLVM’s **remarks** infrastructure to make these insights easy to produce and consume.
+
+**Key points**
+
+- **Opt-in**: Disabled by default. No cost unless enabled.
+- **Per-context**: Configured on `MLIRContext`.
+- **Formats**: YAML or LLVM remark bitstream.
+- **Kinds**: Pass, Missed, Failure, Analysis.
+- **API**: Lightweight stream interface (similar to diagnostics) with `<<`.
+
+## Enabling remarks
+
+Enable once per `MLIRContext` (e.g., in your tool, pass pipeline setup, or test):
+
+```c++
+#include "mlir/IR/MLIRContext.h"
+
+// Writes remarks to /tmp/remarks.yaml in YAML format and mirrors them to
+// the DiagnosticEngine as 'remark' diagnostics with the given category labels.
+context.setupOptimizationRemarks(
+ /*outputPath=*/"/tmp/remarks.yaml",
+ /*outputFormat=*/yaml, // or "bitstream"
+ /*printAsEmitRemarks=*/true,
+ /*categoryPassName=*/"opt.pass", // optional category labels for mirroring
+ /*categoryMissName=*/"opt.missed",
+ /*categoryAnalysisName=*/"opt.analysis",
+ /*categoryFailedName=*/"opt.failed");
+```
+
+### Emitting remarks from a pass
+
+The functions `reportOptimization*` return an in-flight remark object (like MLIR diagnostics). One can append strings and key–value pairs with <<.
+
+```c++
+#include "mlir/IR/Remarks.h"
+
+using namespace mlir;
+
+LogicalResult MyPass::runOnOperation() {
+ Operation *op = getOperation();
+ Location loc = op->getLoc();
+
+ // PASS: something succeeded
+ reportOptimizationPass(loc, /*category=*/"vectorizer", /*passName=*/"MyPass")
+ << "vectorized loop with tripCount="
+ << RemarkBase::RemarkKeyValue("tripCount", 128);
+
+ // ANALYSIS: neutral insight
+ reportOptimizationAnalysis(loc, "unroll", "MyPass")
+ << "estimated cost: " << RemarkBase::RemarkKeyValue("cost", 42);
+
+ // MISSED: explain why + suggest a fix
+ reportOptimizationMiss(loc, "unroll", "MyPass",
+ /*suggestion=*/"increase unroll factor to >=4")
+ << "not profitable at this size";
+
+ // FAILURE: action attempted but failed
+ if (failed(doThing(op))) {
+ reportOptimizationFail(loc, "pipeline", "MyPass")
+ << "failed due to unsupported pattern";
+ return failure();
+ }
+ return success();
+}
+```
+
+## Output formats
+
+#### YAML
+
+A typical remark serialized to YAML looks like following. It is Readable, easy to diff and grep.
+
+```yaml
+--- !Passed
+pass: MyPass
+name: vectorizer
+function: myFunc
+loc: myfile.mlir:12:3
+args:
+ - key: tripCount
+ value: 128
+message: "vectorized loop with tripCount=128"
+```
+
+#### Bitstream
+
+Compact binary format supported by LLVM’s remark tooling. Prefer this for large production runs or where existing infrastructure already understands LLVM remarks.
>From d7a3c03119e810e29511400865678b478b1fc593 Mon Sep 17 00:00:00 2001
From: Guray Ozen <gozen at nvidia.com>
Date: Fri, 8 Aug 2025 12:37:34 +0000
Subject: [PATCH 04/22] address comments
---
mlir/include/mlir/IR/MLIRContext.h | 12 +--
mlir/include/mlir/IR/Remarks.h | 93 +++++++++---------------
mlir/lib/IR/MLIRContext.cpp | 21 +++---
mlir/lib/IR/Remarks.cpp | 113 ++++++++++++++---------------
mlir/unittests/IR/RemarkTest.cpp | 81 +++++++++++++++------
5 files changed, 168 insertions(+), 152 deletions(-)
diff --git a/mlir/include/mlir/IR/MLIRContext.h b/mlir/include/mlir/IR/MLIRContext.h
index ab8f4aed3fc79..590d7492d9e79 100644
--- a/mlir/include/mlir/IR/MLIRContext.h
+++ b/mlir/include/mlir/IR/MLIRContext.h
@@ -12,6 +12,7 @@
#include "mlir/Support/LLVM.h"
#include "mlir/Support/TypeID.h"
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/Remarks/RemarkFormat.h"
#include <functional>
#include <memory>
#include <vector>
@@ -214,7 +215,7 @@ class MLIRContext {
DiagnosticEngine &getDiagEngine();
/// Returns the remark engine for this context.
- std::unique_ptr<RemarkEngine> &getRemarkEngine();
+ RemarkEngine *getRemarkEngine();
/// Returns the storage uniquer used for creating affine constructs.
StorageUniquer &getAffineUniquer();
@@ -249,11 +250,12 @@ class MLIRContext {
/// (attributes, operations, types, etc.).
llvm::hash_code getRegistryHash();
- /// Set up optimization remarks for the context.
+ /// Setup optimization remarks for the context.
void setupOptimizationRemarks(
- StringRef outputPath, StringRef outputFormat, bool printAsEmitRemarks,
- const std::optional<std::string> &categoryPassName = std::nullopt,
- const std::optional<std::string> &categoryMissName = std::nullopt,
+ StringRef outputPath, llvm::remarks::Format outputFormat,
+ bool printAsEmitRemarks,
+ const std::optional<std::string> &categoryPassedName = std::nullopt,
+ const std::optional<std::string> &categoryMissedName = std::nullopt,
const std::optional<std::string> &categoryAnalysisName = std::nullopt,
const std::optional<std::string> &categoryFailedName = std::nullopt);
diff --git a/mlir/include/mlir/IR/Remarks.h b/mlir/include/mlir/IR/Remarks.h
index 240857b388587..e84b095976c71 100644
--- a/mlir/include/mlir/IR/Remarks.h
+++ b/mlir/include/mlir/IR/Remarks.h
@@ -27,7 +27,7 @@ namespace mlir {
/// Defines different remark kinds that can be used to categorize remarks.
enum class RemarkKind {
OptimizationRemarkUnknown = 0,
- OptimizationRemarkPass,
+ OptimizationRemarkPassed,
OptimizationRemarkMissed,
OptimizationRemarkFailure,
OptimizationRemarkAnalysis,
@@ -143,7 +143,7 @@ class RemarkBase : public llvm::DiagnosticInfo {
switch (remarkKind) {
case RemarkKind::OptimizationRemarkUnknown:
return llvm::DiagnosticKind::DK_Generic;
- case RemarkKind::OptimizationRemarkPass:
+ case RemarkKind::OptimizationRemarkPassed:
return llvm::DiagnosticKind::DK_OptimizationRemark;
case RemarkKind::OptimizationRemarkMissed:
return llvm::DiagnosticKind::DK_OptimizationRemarkMissed;
@@ -156,51 +156,19 @@ class RemarkBase : public llvm::DiagnosticInfo {
}
};
-// clang-format off
-template <class RemarkT>
-decltype(auto) operator<<(
- RemarkT &&r,
- std::enable_if_t<std::is_base_of_v<RemarkBase,
- std::remove_reference_t<RemarkT>>,
- StringRef>
- s) {
+inline RemarkBase &operator<<(RemarkBase &r, StringRef s) {
r.insert(s);
- return std::forward<RemarkT>(r);
-}
-
-template <class RemarkT>
-decltype(auto) operator<<(
- RemarkT &&r,
- std::enable_if_t<std::is_base_of_v<RemarkBase,
- std::remove_reference_t<RemarkT>>,
- RemarkBase::RemarkKeyValue>
- a) {
- r.insert(std::move(a));
- return std::forward<RemarkT>(r);
+ return r;
}
-
-template <class RemarkT>
-decltype(auto) operator<<(
- RemarkT &&r,
- std::enable_if_t<std::is_base_of_v<RemarkBase,
- std::remove_reference_t<RemarkT>>,
- RemarkBase::SetIsVerbose>
- v) {
- r.insert(v);
- return std::forward<RemarkT>(r);
+inline RemarkBase &&operator<<(RemarkBase &&r, StringRef s) {
+ r.insert(s);
+ return std::move(r);
}
-
-template <class RemarkT>
-decltype(auto) operator<<(
- RemarkT &&r,
- std::enable_if_t<std::is_base_of_v<RemarkBase,
- std::remove_reference_t<RemarkT>>,
- RemarkBase::SetExtraArgs>
- ea) {
- r.insert(ea);
- return std::forward<RemarkT>(r);
+inline RemarkBase &operator<<(RemarkBase &r,
+ const RemarkBase::RemarkKeyValue &kv) {
+ r.insert(kv);
+ return r;
}
-// clang-format on
//===----------------------------------------------------------------------===//
// Shorthand aliases for different kinds of remarks.
@@ -209,8 +177,9 @@ decltype(auto) operator<<(
template <RemarkKind K, DiagnosticSeverity S>
class OptRemarkBase final : public RemarkBase {
public:
- explicit OptRemarkBase(Location loc, StringRef passName, StringRef remarkName)
- : RemarkBase(K, S, passName.data(), remarkName, loc) {}
+ explicit OptRemarkBase(Location loc, StringRef passName,
+ StringRef categoryName)
+ : RemarkBase(K, S, passName.data(), categoryName, loc) {}
bool isEnabled() const override { return true; }
};
@@ -218,7 +187,7 @@ class OptRemarkBase final : public RemarkBase {
using OptRemarkAnalysis = OptRemarkBase<RemarkKind::OptimizationRemarkAnalysis,
DiagnosticSeverity::Remark>;
-using OptRemarkPass = OptRemarkBase<RemarkKind::OptimizationRemarkPass,
+using OptRemarkPass = OptRemarkBase<RemarkKind::OptimizationRemarkPassed,
DiagnosticSeverity::Remark>;
using OptRemarkMissed = OptRemarkBase<RemarkKind::OptimizationRemarkMissed,
@@ -241,8 +210,8 @@ class RemarkEngine;
class InFlightRemark {
public:
explicit InFlightRemark(RemarkBase *diag) : remark(diag) {}
- InFlightRemark(RemarkEngine &eng, RemarkBase *diag)
- : owner(&eng), remark(diag) {}
+ InFlightRemark(RemarkEngine &eng, std::unique_ptr<RemarkBase> diag)
+ : owner(&eng), remark(std::move(diag)) {}
InFlightRemark() = default; // empty ctor
@@ -287,7 +256,8 @@ class MLIRRemarkStreamer {
class RemarkEngine {
private:
- /// The category for missed optimization remarks.
+ /// Regex that filters missed optimization remarks: only matching one are
+ /// reported.
std::optional<llvm::Regex> missFilter;
/// The category for passed optimization remarks.
std::optional<llvm::Regex> passFilter;
@@ -299,16 +269,17 @@ class RemarkEngine {
std::unique_ptr<llvm::ToolOutputFile> remarksFile;
/// The MLIR remark streamer that will be used to emit the remarks.
std::unique_ptr<MLIRRemarkStreamer> remarkStreamer;
+ /// The LLVM remark streamer that will be used to emit the remarks.
std::unique_ptr<llvm::remarks::RemarkStreamer> llvmRemarkStreamer;
/// When is enabled, engine also prints remarks as mlir::emitRemarks.
- bool printAsEmitRemarks;
+ bool printAsEmitRemarks = false;
+
/// The main MLIR remark streamer that will be used to emit the remarks.
MLIRRemarkStreamer *getLLVMRemarkStreamer() { return remarkStreamer.get(); }
const MLIRRemarkStreamer *getLLVMRemarkStreamer() const {
return remarkStreamer.get();
}
- void
- setLLVMRemarkStreamer(std::unique_ptr<MLIRRemarkStreamer> remarkStreamer) {
+ void setRemarkStreamer(std::unique_ptr<MLIRRemarkStreamer> remarkStreamer) {
this->remarkStreamer = std::move(remarkStreamer);
}
@@ -361,8 +332,12 @@ class RemarkEngine {
bool (RemarkEngine::*isEnabled)(StringRef) const);
public:
+ /// Default constructor is deleted, use the other constructor.
RemarkEngine() = delete;
- /// Constructs Remark engine with optional category names
+
+ /// Constructs Remark engine with optional category names. If a category
+ /// name is not provided, it is not enabled. The category names are used to
+ /// filter the remarks that are emitted.
RemarkEngine(bool printAsEmitRemarks,
std::optional<std::string> categoryPassName = std::nullopt,
std::optional<std::string> categoryMissName = std::nullopt,
@@ -374,7 +349,8 @@ class RemarkEngine {
~RemarkEngine();
/// Setup the remark engine with the given output path and format.
- llvm::Error initialize(StringRef outputPath, StringRef outputFormat);
+ LogicalResult initialize(StringRef outputPath, llvm::remarks::Format fmt,
+ std::string *errMsg);
/// Report a diagnostic remark.
void report(const RemarkBase &&diag);
@@ -406,14 +382,13 @@ using Suggestion = RemarkBase::RemarkKeyValue;
inline Suggestion suggest(StringRef txt) { return {"Suggestion", txt}; }
template <typename Fn, typename... Args>
-[[nodiscard]] inline InFlightRemark withEngine(Fn fn, Location loc,
- Args &&...args) {
+inline InFlightRemark withEngine(Fn fn, Location loc, Args &&...args) {
MLIRContext *ctx = loc->getContext();
- auto &enginePtr = ctx->getRemarkEngine();
+ RemarkEngine *enginePtr = ctx->getRemarkEngine();
- if (RemarkEngine *eng = enginePtr.get())
- return (eng->*fn)(loc, std::forward<Args>(args)...);
+ if (enginePtr)
+ return (enginePtr->*fn)(loc, std::forward<Args>(args)...);
return {};
}
diff --git a/mlir/lib/IR/MLIRContext.cpp b/mlir/lib/IR/MLIRContext.cpp
index 5a37de1481eb9..3eb271d58a871 100644
--- a/mlir/lib/IR/MLIRContext.cpp
+++ b/mlir/lib/IR/MLIRContext.cpp
@@ -403,22 +403,25 @@ void MLIRContext::setRemarkEngine(std::unique_ptr<RemarkEngine> engine) {
getImpl().remarkEngine = std::move(engine);
}
-std::unique_ptr<RemarkEngine> &MLIRContext::getRemarkEngine() {
- return getImpl().remarkEngine;
+RemarkEngine *MLIRContext::getRemarkEngine() {
+ return getImpl().remarkEngine.get();
}
void MLIRContext::setupOptimizationRemarks(
- StringRef outputPath, StringRef outputFormat, bool printAsEmitRemarks,
- const std::optional<std::string> &categoryPassName,
- const std::optional<std::string> &categoryMissName,
+ StringRef outputPath, llvm::remarks::Format outputFormat,
+ bool printAsEmitRemarks,
+ const std::optional<std::string> &categoryPassedName,
+ const std::optional<std::string> &categoryMissedName,
const std::optional<std::string> &categoryAnalysisName,
const std::optional<std::string> &categoryFailedName) {
auto engine = std::make_unique<RemarkEngine>(
- printAsEmitRemarks, categoryPassName, categoryMissName,
+ printAsEmitRemarks, categoryPassedName, categoryMissedName,
categoryAnalysisName, categoryFailedName);
- llvm::Error e = engine->initialize(outputPath, outputFormat);
- if (e)
- llvm::report_fatal_error("Failed to initialize remark engine");
+ std::string errMsg;
+ if (failed(engine->initialize(outputPath, outputFormat, &errMsg))) {
+ llvm::report_fatal_error(
+ llvm::Twine("Failed to initialize remark engine. Error: ") + errMsg);
+ }
setRemarkEngine(std::move(engine));
}
diff --git a/mlir/lib/IR/Remarks.cpp b/mlir/lib/IR/Remarks.cpp
index c0bae3915f0e0..f2d085d0b3357 100644
--- a/mlir/lib/IR/Remarks.cpp
+++ b/mlir/lib/IR/Remarks.cpp
@@ -84,14 +84,14 @@ std::string RemarkBase::getMsg() const {
for (const RemarkBase::RemarkKeyValue &arg :
llvm::make_range(args.begin(), args.end()))
rss << arg.val;
- return str;
+ return rss.str();
}
llvm::remarks::Type RemarkBase::getRemarkType() const {
switch (remarkKind) {
case RemarkKind::OptimizationRemarkUnknown:
return llvm::remarks::Type::Unknown;
- case RemarkKind::OptimizationRemarkPass:
+ case RemarkKind::OptimizationRemarkPassed:
return llvm::remarks::Type::Passed;
case RemarkKind::OptimizationRemarkMissed:
return llvm::remarks::Type::Missed;
@@ -129,10 +129,8 @@ llvm::remarks::Remark RemarkBase::generateRemark() const {
//===----------------------------------------------------------------------===//
InFlightRemark::~InFlightRemark() {
- if (remark)
- if (owner) {
- owner->report(std::move(*remark));
- }
+ if (remark && owner)
+ owner->report(std::move(*remark));
owner = nullptr;
}
@@ -142,16 +140,19 @@ InFlightRemark::~InFlightRemark() {
template <typename RemarkT, typename... Args>
InFlightRemark RemarkEngine::makeRemark(Args &&...args) {
- return InFlightRemark(*this, new RemarkT(std::forward<Args>(args)...));
+ static_assert(std::is_base_of_v<RemarkBase, RemarkT>,
+ "RemarkT must derive from RemarkBase");
+ return InFlightRemark(*this,
+ std::make_unique<RemarkT>(std::forward<Args>(args)...));
}
template <typename RemarkT>
InFlightRemark
RemarkEngine::emitIfEnabled(Location loc, StringRef passName,
- StringRef category,
+ StringRef categoryName,
bool (RemarkEngine::*isEnabled)(StringRef) const) {
- return (this->*isEnabled)(category)
- ? makeRemark<RemarkT>(loc, category, passName)
+ return (this->*isEnabled)(categoryName)
+ ? makeRemark<RemarkT>(loc, categoryName, passName)
: InFlightRemark{};
}
@@ -173,30 +174,30 @@ bool RemarkEngine::isFailedOptRemarkEnabled(StringRef categoryName) const {
InFlightRemark RemarkEngine::emitOptimizationRemark(Location loc,
StringRef passName,
- StringRef category) {
- return emitIfEnabled<OptRemarkPass>(loc, passName, category,
+ StringRef categoryName) {
+ return emitIfEnabled<OptRemarkPass>(loc, passName, categoryName,
&RemarkEngine::isPassedOptRemarkEnabled);
}
-InFlightRemark RemarkEngine::emitOptimizationRemarkMiss(Location loc,
- StringRef passName,
- StringRef category) {
+InFlightRemark
+RemarkEngine::emitOptimizationRemarkMiss(Location loc, StringRef passName,
+ StringRef categoryName) {
return emitIfEnabled<OptRemarkMissed>(
- loc, passName, category, &RemarkEngine::isMissedOptRemarkEnabled);
+ loc, passName, categoryName, &RemarkEngine::isMissedOptRemarkEnabled);
}
-InFlightRemark RemarkEngine::emitOptimizationRemarkFailure(Location loc,
- StringRef passName,
- StringRef category) {
+InFlightRemark
+RemarkEngine::emitOptimizationRemarkFailure(Location loc, StringRef passName,
+ StringRef categoryName) {
return emitIfEnabled<OptRemarkFailure>(
- loc, passName, category, &RemarkEngine::isFailedOptRemarkEnabled);
+ loc, passName, categoryName, &RemarkEngine::isFailedOptRemarkEnabled);
}
InFlightRemark
RemarkEngine::emitOptimizationRemarkAnalysis(Location loc, StringRef passName,
- StringRef category) {
+ StringRef categoryName) {
return emitIfEnabled<OptRemarkAnalysis>(
- loc, passName, category, &RemarkEngine::isAnalysisOptRemarkEnabled);
+ loc, passName, categoryName, &RemarkEngine::isAnalysisOptRemarkEnabled);
}
//===----------------------------------------------------------------------===//
@@ -221,9 +222,8 @@ void RemarkEngine::report(const RemarkBase &&diag) {
getLLVMRemarkStreamer()->streamOptimizationRemark(diag);
// Print using MLIR's diagnostic
- if (printAsEmitRemarks) {
+ if (printAsEmitRemarks)
emitRemark(diag.getLocation(), diag.getMsg());
- }
}
RemarkEngine::~RemarkEngine() {
@@ -232,49 +232,46 @@ RemarkEngine::~RemarkEngine() {
remarksFile.reset();
}
setMainRemarkStreamer(nullptr);
- setLLVMRemarkStreamer(nullptr);
+ setRemarkStreamer(nullptr);
}
-llvm::Error RemarkEngine::initialize(StringRef outputPath,
- StringRef outputFormat) {
- if (remarksFile) {
- return llvm::createStringError(
- llvm::inconvertibleErrorCode(),
- "RemarkEngine is already initialized with an output file");
- }
+LogicalResult RemarkEngine::initialize(StringRef outputPath,
+ llvm::remarks::Format fmt,
+ std::string *errMsg) {
+ auto fail = [&](llvm::StringRef msg) {
+ if (errMsg)
+ *errMsg = msg.str();
+ return failure();
+ };
- auto fFormatOrErr = llvm::remarks::parseFormat(outputFormat);
- if (!fFormatOrErr) {
- return fFormatOrErr.takeError();
- }
- llvm::remarks::Format fFormat = *fFormatOrErr;
- std::error_code errCode;
- llvm::sys::fs::OpenFlags fFlags = (fFormat == llvm::remarks::Format::YAML)
- ? llvm::sys::fs::OF_Text
- : llvm::sys::fs::OF_None;
-
- remarksFile =
- std::make_unique<llvm::ToolOutputFile>(outputPath, errCode, fFlags);
- if (errCode) {
- remarksFile.reset(); // force an empty optional
- return llvm::createStringError(errCode, "Failed to open remarks file");
+ if (remarksFile)
+ return fail("RemarkEngine is already initialized with an output file.");
+
+ llvm::sys::fs::OpenFlags flags = (fmt == llvm::remarks::Format::YAML)
+ ? llvm::sys::fs::OF_Text
+ : llvm::sys::fs::OF_None;
+ std::error_code ec;
+ remarksFile = std::make_unique<llvm::ToolOutputFile>(outputPath, ec, flags);
+
+ if (ec) {
+ remarksFile.reset();
+ return fail(
+ ("Failed to open remarks file '" + outputPath + "': " + ec.message())
+ .str());
}
- auto remarkSerializer = llvm::remarks::createRemarkSerializer(
- *fFormatOrErr, llvm::remarks::SerializerMode::Separate,
- remarksFile->os());
- if (llvm::Error err = remarkSerializer.takeError()) {
- return err;
+ auto serializer = llvm::remarks::createRemarkSerializer(
+ fmt, llvm::remarks::SerializerMode::Separate, remarksFile->os());
+ if (!serializer) {
+ remarksFile.reset();
+ return fail(llvm::toString(serializer.takeError()));
}
- // Create the main remark streamer.
setMainRemarkStreamer(std::make_unique<llvm::remarks::RemarkStreamer>(
- std::move(*remarkSerializer), outputFormat));
-
- setLLVMRemarkStreamer(
+ std::move(*serializer), outputPath));
+ setRemarkStreamer(
std::make_unique<MLIRRemarkStreamer>(*getMainRemarkStreamer()));
-
- return llvm::ErrorSuccess();
+ return success();
}
RemarkEngine::RemarkEngine(bool printAsEmitRemarks,
diff --git a/mlir/unittests/IR/RemarkTest.cpp b/mlir/unittests/IR/RemarkTest.cpp
index 91bb6d82a6920..2472b140e07ac 100644
--- a/mlir/unittests/IR/RemarkTest.cpp
+++ b/mlir/unittests/IR/RemarkTest.cpp
@@ -10,6 +10,7 @@
#include "mlir/IR/Remarks.h"
#include "mlir/Support/TypeID.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/Remarks/RemarkFormat.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/YAMLParser.h"
#include "gtest/gtest.h"
@@ -26,8 +27,6 @@ TEST(Remark, TestOutputOptimizationRemark) {
const auto *pass2Msg = "My another message";
const auto *pass3Msg = "Do not show this message";
- auto *context = new MLIRContext();
-
std::string categoryLoopunroll("LoopUnroll");
std::string categoryInline("Inliner");
std::string myPassname1("myPass1");
@@ -39,21 +38,26 @@ TEST(Remark, TestOutputOptimizationRemark) {
std::string yamlFile =
std::string(tmpPathStorage.data(), tmpPathStorage.size());
ASSERT_FALSE(yamlFile.empty());
- Location loc = UnknownLoc::get(context);
- // Setup the remark engine
- context->setupOptimizationRemarks(yamlFile, "yaml", true, categoryLoopunroll,
- std::nullopt, std::nullopt,
- categoryLoopunroll);
+ {
+ MLIRContext context;
+ Location loc = UnknownLoc::get(&context);
- // Remark 1: pass, category LoopUnroll
- reportOptimizationPass(loc, categoryLoopunroll, myPassname1) << pass1Msg;
- // Remark 2: failure, category LoopUnroll
- reportOptimizationFail(loc, categoryLoopunroll, myPassname2) << pass2Msg;
- // Remark 3: pass, category Inline (should not be printed)
- reportOptimizationPass(loc, categoryInline, myPassname1) << pass3Msg;
+ context.printOpOnDiagnostic(true);
+ context.printStackTraceOnDiagnostic(true);
+
+ // Setup the remark engine
+ context.setupOptimizationRemarks(yamlFile, llvm::remarks::Format::YAML,
+ true, categoryLoopunroll, std::nullopt,
+ std::nullopt, categoryLoopunroll);
- delete context;
+ // Remark 1: pass, category LoopUnroll
+ reportOptimizationPass(loc, categoryLoopunroll, myPassname1) << pass1Msg;
+ // Remark 2: failure, category LoopUnroll
+ reportOptimizationFail(loc, categoryLoopunroll, myPassname2) << pass2Msg;
+ // Remark 3: pass, category Inline (should not be printed)
+ reportOptimizationPass(loc, categoryInline, myPassname1) << pass3Msg;
+ }
// Read the file
auto bufferOrErr = MemoryBuffer::getFile(yamlFile);
@@ -81,7 +85,6 @@ TEST(Remark, TestOutputOptimizationRemark) {
TEST(Remark, TestNoOutputOptimizationRemark) {
const auto *pass1Msg = "My message";
- auto *context = new MLIRContext();
std::string categoryFailName("myImportantCategory");
std::string myPassname1("myPass1");
@@ -97,12 +100,11 @@ TEST(Remark, TestNoOutputOptimizationRemark) {
if (ec) {
FAIL() << "Failed to remove file " << yamlFile << ": " << ec.message();
}
-
- Location loc = UnknownLoc::get(context);
- reportOptimizationFail(loc, categoryFailName, myPassname1) << pass1Msg;
-
- delete context;
-
+ {
+ MLIRContext context;
+ Location loc = UnknownLoc::get(&context);
+ reportOptimizationFail(loc, categoryFailName, myPassname1) << pass1Msg;
+ }
// No setup, so no output file should be created
// check!
bool fileExists = llvm::sys::fs::exists(yamlFile);
@@ -110,4 +112,41 @@ TEST(Remark, TestNoOutputOptimizationRemark) {
<< "Expected no YAML file to be created without setupOptimizationRemarks";
}
+TEST(Remark, TestOutputOptimizationRemarkDiagnostic) {
+ const auto *pass1Msg = "My message";
+
+ std::string categoryLoopunroll("LoopUnroll");
+ std::string myPassname1("myPass1");
+ std::string funcName("myFunc");
+ SmallString<64> tmpPathStorage;
+ sys::fs::createUniquePath("remarks-%%%%%%.yaml", tmpPathStorage,
+ /*MakeAbsolute=*/true);
+ std::string yamlFile =
+ std::string(tmpPathStorage.data(), tmpPathStorage.size());
+ ASSERT_FALSE(yamlFile.empty());
+ std::string seenMsg;
+ {
+ MLIRContext context;
+ Location loc = UnknownLoc::get(&context);
+
+ context.printOpOnDiagnostic(true);
+ context.printStackTraceOnDiagnostic(true);
+
+ // Register a handler that captures the diagnostic.
+ ScopedDiagnosticHandler handler(&context, [&](Diagnostic &diag) {
+ seenMsg = diag.str();
+ return success();
+ });
+
+ // Setup the remark engine
+ context.setupOptimizationRemarks(yamlFile, llvm::remarks::Format::YAML,
+ true, categoryLoopunroll, std::nullopt,
+ std::nullopt, categoryLoopunroll);
+
+ // Remark 1: pass, category LoopUnroll
+ reportOptimizationPass(loc, categoryLoopunroll, myPassname1) << pass1Msg;
+ }
+ EXPECT_EQ(seenMsg, pass1Msg);
+}
+
} // namespace
>From f22137f090387610d1f849ca1e58bd5926b52926 Mon Sep 17 00:00:00 2001
From: Guray Ozen <gozen at nvidia.com>
Date: Mon, 11 Aug 2025 11:49:37 +0000
Subject: [PATCH 05/22] Split Streamer from RemarlEngine
---
mlir/include/mlir/IR/MLIRContext.h | 23 +++-
mlir/include/mlir/IR/Remarks.h | 88 ++++--------
mlir/include/mlir/Remark/RemarkStreamer.h | 33 +++++
mlir/lib/CMakeLists.txt | 1 +
mlir/lib/IR/CMakeLists.txt | 5 -
mlir/lib/IR/MLIRContext.cpp | 18 +--
mlir/lib/IR/Remarks.cpp | 161 ++++++++++------------
mlir/lib/Remark/CMakeLists.txt | 14 ++
mlir/lib/Remark/RemarkStreamer.cpp | 66 +++++++++
mlir/unittests/IR/CMakeLists.txt | 1 +
mlir/unittests/IR/RemarkTest.cpp | 103 ++++++++++++--
11 files changed, 324 insertions(+), 189 deletions(-)
create mode 100644 mlir/include/mlir/Remark/RemarkStreamer.h
create mode 100644 mlir/lib/Remark/CMakeLists.txt
create mode 100644 mlir/lib/Remark/RemarkStreamer.cpp
diff --git a/mlir/include/mlir/IR/MLIRContext.h b/mlir/include/mlir/IR/MLIRContext.h
index 590d7492d9e79..cfb408b1f13eb 100644
--- a/mlir/include/mlir/IR/MLIRContext.h
+++ b/mlir/include/mlir/IR/MLIRContext.h
@@ -33,6 +33,7 @@ class InFlightDiagnostic;
class Location;
class MLIRContextImpl;
class RegisteredOperationName;
+class MLIRRemarkStreamerBase;
class RemarkEngine;
class StorageUniquer;
class IRUnit;
@@ -62,6 +63,10 @@ class IRUnit;
class MLIRContext {
public:
enum class Threading { DISABLED, ENABLED };
+ struct RemarkCategories {
+ std::optional<std::string> passed, missed, analysis, failed;
+ };
+
/// Create a new Context.
explicit MLIRContext(Threading multithreading = Threading::ENABLED);
explicit MLIRContext(const DialectRegistry ®istry,
@@ -251,13 +256,17 @@ class MLIRContext {
llvm::hash_code getRegistryHash();
/// Setup optimization remarks for the context.
- void setupOptimizationRemarks(
- StringRef outputPath, llvm::remarks::Format outputFormat,
- bool printAsEmitRemarks,
- const std::optional<std::string> &categoryPassedName = std::nullopt,
- const std::optional<std::string> &categoryMissedName = std::nullopt,
- const std::optional<std::string> &categoryAnalysisName = std::nullopt,
- const std::optional<std::string> &categoryFailedName = std::nullopt);
+ /// This 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 `printAsEmitRemarks` is true, the remarks will be printed as
+ /// mlir::emitRemarks.
+ LogicalResult
+ enableOptimizationRemarks(std::unique_ptr<MLIRRemarkStreamerBase> streamer,
+ const RemarkCategories &cats,
+ bool printAsEmitRemarks = false);
//===--------------------------------------------------------------------===//
// Action API
diff --git a/mlir/include/mlir/IR/Remarks.h b/mlir/include/mlir/IR/Remarks.h
index e84b095976c71..ccc2b79e1d05a 100644
--- a/mlir/include/mlir/IR/Remarks.h
+++ b/mlir/include/mlir/IR/Remarks.h
@@ -14,9 +14,8 @@
#define MLIR_IR_REMARKS_H
#include "llvm/IR/DiagnosticInfo.h"
-#include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/Remarks/Remark.h"
-#include "llvm/Remarks/RemarkStreamer.h"
+#include "llvm/Support/Regex.h"
#include "llvm/Support/ToolOutputFile.h"
#include "mlir/IR/Diagnostics.h"
@@ -36,21 +35,15 @@ enum class RemarkKind {
//===----------------------------------------------------------------------===//
// Remark Base Class
//===----------------------------------------------------------------------===//
-class RemarkBase : public llvm::DiagnosticInfo {
+class RemarkBase {
public:
RemarkBase(RemarkKind remarkKind, DiagnosticSeverity severity,
const char *passName, StringRef remarkName, Location loc,
std::optional<StringRef> functionName = std::nullopt)
- : llvm::DiagnosticInfo(makeLLVMKind(remarkKind),
- makeLLVMSeverity(severity)),
- remarkKind(remarkKind), functionName(functionName), loc(loc),
+ : remarkKind(remarkKind), functionName(functionName), loc(loc),
passName(passName), remarkName(remarkName) {}
- struct SetIsVerbose {};
-
- struct SetExtraArgs {};
-
struct RemarkKeyValue {
std::string key;
std::string val;
@@ -74,13 +67,9 @@ class RemarkBase : public llvm::DiagnosticInfo {
void insert(StringRef s);
void insert(RemarkKeyValue a);
- void insert(SetIsVerbose v);
- void insert(SetExtraArgs ea);
- void print(llvm::DiagnosticPrinter &dp) const override;
- void print() const;
+ void print(llvm::raw_ostream &os, bool printLocation = false) const;
- virtual bool isEnabled() const = 0;
Location getLocation() const { return loc; }
/// Diagnostic -> Remark
llvm::remarks::Remark generateRemark() const;
@@ -94,12 +83,12 @@ class RemarkBase : public llvm::DiagnosticInfo {
StringRef getRemarkName() const { return remarkName; }
std::string getMsg() const;
- bool isVerbose() const { return isVerboseRemark; }
-
ArrayRef<RemarkKeyValue> getArgs() const { return args; }
llvm::remarks::Type getRemarkType() const;
+ std::string getRemarkTypeString() const;
+
protected:
/// Keeps the MLIR diagnostic kind, which is used to determine the
/// diagnostic kind in the LLVM remark streamer.
@@ -119,9 +108,6 @@ class RemarkBase : public llvm::DiagnosticInfo {
/// RemarkKeyValues collected via the streaming interface.
SmallVector<RemarkKeyValue, 4> args;
- /// The remark is expected to be noisy.
- bool isVerboseRemark = false;
-
private:
/// Convert the MLIR diagnostic severity to LLVM diagnostic severity.
static llvm::DiagnosticSeverity
@@ -180,8 +166,6 @@ class OptRemarkBase final : public RemarkBase {
explicit OptRemarkBase(Location loc, StringRef passName,
StringRef categoryName)
: RemarkBase(K, S, passName.data(), categoryName, loc) {}
-
- bool isEnabled() const override { return true; }
};
using OptRemarkAnalysis = OptRemarkBase<RemarkKind::OptimizationRemarkAnalysis,
@@ -240,14 +224,21 @@ class InFlightRemark {
// MLIR Remark Streamer
//===----------------------------------------------------------------------===//
-class MLIRRemarkStreamer {
- llvm::remarks::RemarkStreamer &remarkStreamer;
-
+/// Base class for MLIR remark streamers that is used to stream
+/// optimization remarks to the underlying remark streamer. The derived classes
+/// should implement the `streamOptimizationRemark` method to provide the
+/// actual streaming implementation.
+class MLIRRemarkStreamerBase {
public:
- explicit MLIRRemarkStreamer(llvm::remarks::RemarkStreamer &remarkStreamer)
- : remarkStreamer(remarkStreamer) {}
-
- void streamOptimizationRemark(const RemarkBase &remark);
+ virtual ~MLIRRemarkStreamerBase() = default;
+ /// Stream an optimization remark to the underlying remark streamer. It is
+ /// called by the RemarkEngine to stream the optimization remarks.
+ ///
+ /// It must be overridden by the derived classes to provide
+ /// the actual streaming implementation.
+ virtual void streamOptimizationRemark(const RemarkBase &remark) = 0;
+
+ virtual void finalize() {} // optional
};
//===----------------------------------------------------------------------===//
@@ -265,37 +256,11 @@ class RemarkEngine {
std::optional<llvm::Regex> analysisFilter;
/// The category for failed optimization remarks.
std::optional<llvm::Regex> failedFilter;
- /// The output file for the remarks.
- std::unique_ptr<llvm::ToolOutputFile> remarksFile;
/// The MLIR remark streamer that will be used to emit the remarks.
- std::unique_ptr<MLIRRemarkStreamer> remarkStreamer;
- /// The LLVM remark streamer that will be used to emit the remarks.
- std::unique_ptr<llvm::remarks::RemarkStreamer> llvmRemarkStreamer;
+ std::unique_ptr<MLIRRemarkStreamerBase> remarkStreamer;
/// When is enabled, engine also prints remarks as mlir::emitRemarks.
bool printAsEmitRemarks = false;
- /// The main MLIR remark streamer that will be used to emit the remarks.
- MLIRRemarkStreamer *getLLVMRemarkStreamer() { return remarkStreamer.get(); }
- const MLIRRemarkStreamer *getLLVMRemarkStreamer() const {
- return remarkStreamer.get();
- }
- void setRemarkStreamer(std::unique_ptr<MLIRRemarkStreamer> remarkStreamer) {
- this->remarkStreamer = std::move(remarkStreamer);
- }
-
- /// Get the main MLIR remark streamer that will be used to emit the remarks.
- llvm::remarks::RemarkStreamer *getMainRemarkStreamer() {
- return llvmRemarkStreamer.get();
- }
- const llvm::remarks::RemarkStreamer *getMainRemarkStreamer() const {
- return llvmRemarkStreamer.get();
- }
- /// Set the main remark streamer to be used by the engine.
- void setMainRemarkStreamer(
- std::unique_ptr<llvm::remarks::RemarkStreamer> mainRemarkStreamer) {
- llvmRemarkStreamer = std::move(mainRemarkStreamer);
- }
-
/// Return true if missed optimization remarks are enabled, override
/// to provide different implementation.
bool isMissedOptRemarkEnabled(StringRef categoryName) const;
@@ -339,21 +304,18 @@ class RemarkEngine {
/// name is not provided, it is not enabled. The category names are used to
/// filter the remarks that are emitted.
RemarkEngine(bool printAsEmitRemarks,
- std::optional<std::string> categoryPassName = std::nullopt,
- std::optional<std::string> categoryMissName = std::nullopt,
- std::optional<std::string> categoryAnalysisName = std::nullopt,
- std::optional<std::string> categoryFailedName = std::nullopt);
+ const MLIRContext::RemarkCategories &cats);
/// Destructor that will close the output file and reset the
/// main remark streamer.
~RemarkEngine();
/// Setup the remark engine with the given output path and format.
- LogicalResult initialize(StringRef outputPath, llvm::remarks::Format fmt,
+ LogicalResult initialize(std::unique_ptr<MLIRRemarkStreamerBase> streamer,
std::string *errMsg);
- /// Report a diagnostic remark.
- void report(const RemarkBase &&diag);
+ /// Report a remark.
+ void report(const RemarkBase &&remark);
/// Report a successful remark, this will create an InFlightRemark
/// that can be used to build the remark using the << operator.
diff --git a/mlir/include/mlir/Remark/RemarkStreamer.h b/mlir/include/mlir/Remark/RemarkStreamer.h
new file mode 100644
index 0000000000000..649e8028fe737
--- /dev/null
+++ b/mlir/include/mlir/Remark/RemarkStreamer.h
@@ -0,0 +1,33 @@
+#include "mlir/IR/Remarks.h"
+#include "llvm/Remarks/RemarkStreamer.h"
+
+using namespace llvm;
+namespace mlir::remark {
+
+/// Concrete streamer that writes LLVM optimization remarks to a file
+/// (YAML or Bitstream). Lives outside core.
+class LLVMRemarkStreamer final : public MLIRRemarkStreamerBase {
+public:
+ static FailureOr<std::unique_ptr<MLIRRemarkStreamerBase>>
+ createToFile(llvm::StringRef path, llvm::remarks::Format fmt);
+
+ void streamOptimizationRemark(const RemarkBase &remark) override;
+ void finalize() override {}
+ ~LLVMRemarkStreamer() override;
+
+private:
+ LLVMRemarkStreamer() = default;
+
+ std::unique_ptr<class llvm::remarks::RemarkStreamer> remarkStreamer;
+ std::unique_ptr<class llvm::ToolOutputFile> file;
+};
+
+/// Enable optimization remarks to a file with the given path and format.
+/// The remark categories are used to filter the remarks that are emitted.
+/// If the printAsEmitRemarks flag is set, remarks will also be printed using
+/// mlir::emitRemarks.
+LogicalResult enableOptimizationRemarksToFile(
+ MLIRContext &ctx, StringRef path, llvm::remarks::Format fmt,
+ const MLIRContext::RemarkCategories &cat, bool printAsEmitRemarks = false);
+
+} // namespace mlir::remark
\ No newline at end of file
diff --git a/mlir/lib/CMakeLists.txt b/mlir/lib/CMakeLists.txt
index 191b5ab6a6f56..91ed05f6548d7 100644
--- a/mlir/lib/CMakeLists.txt
+++ b/mlir/lib/CMakeLists.txt
@@ -13,6 +13,7 @@ add_subdirectory(Parser)
add_subdirectory(Pass)
add_subdirectory(Query)
add_subdirectory(Reducer)
+add_subdirectory(Remark)
add_subdirectory(Rewrite)
add_subdirectory(Support)
add_subdirectory(TableGen)
diff --git a/mlir/lib/IR/CMakeLists.txt b/mlir/lib/IR/CMakeLists.txt
index 438e486f2ad4c..d95bdc957e3c2 100644
--- a/mlir/lib/IR/CMakeLists.txt
+++ b/mlir/lib/IR/CMakeLists.txt
@@ -70,10 +70,5 @@ add_mlir_library(MLIRIR
LINK_LIBS PUBLIC
MLIRSupport
-
- LINK_COMPONENTS
- Remarks
- Core
- BitstreamReader
)
diff --git a/mlir/lib/IR/MLIRContext.cpp b/mlir/lib/IR/MLIRContext.cpp
index 3eb271d58a871..e71db084c4b81 100644
--- a/mlir/lib/IR/MLIRContext.cpp
+++ b/mlir/lib/IR/MLIRContext.cpp
@@ -407,22 +407,18 @@ RemarkEngine *MLIRContext::getRemarkEngine() {
return getImpl().remarkEngine.get();
}
-void MLIRContext::setupOptimizationRemarks(
- StringRef outputPath, llvm::remarks::Format outputFormat,
- bool printAsEmitRemarks,
- const std::optional<std::string> &categoryPassedName,
- const std::optional<std::string> &categoryMissedName,
- const std::optional<std::string> &categoryAnalysisName,
- const std::optional<std::string> &categoryFailedName) {
- auto engine = std::make_unique<RemarkEngine>(
- printAsEmitRemarks, categoryPassedName, categoryMissedName,
- categoryAnalysisName, categoryFailedName);
+LogicalResult MLIRContext::enableOptimizationRemarks(
+ std::unique_ptr<MLIRRemarkStreamerBase> streamer,
+ const RemarkCategories &cats, bool printAsEmitRemarks) {
+ auto engine = std::make_unique<RemarkEngine>(printAsEmitRemarks, cats);
+
std::string errMsg;
- if (failed(engine->initialize(outputPath, outputFormat, &errMsg))) {
+ if (failed(engine->initialize(std::move(streamer), &errMsg))) {
llvm::report_fatal_error(
llvm::Twine("Failed to initialize remark engine. Error: ") + errMsg);
}
setRemarkEngine(std::move(engine));
+ return success();
}
//===----------------------------------------------------------------------===//
diff --git a/mlir/lib/IR/Remarks.cpp b/mlir/lib/IR/Remarks.cpp
index f2d085d0b3357..6090682f72138 100644
--- a/mlir/lib/IR/Remarks.cpp
+++ b/mlir/lib/IR/Remarks.cpp
@@ -8,15 +8,12 @@
#include "mlir/IR/Remarks.h"
-#include "mlir/IR/Attributes.h"
#include "mlir/IR/BuiltinAttributes.h"
#include "mlir/IR/Diagnostics.h"
#include "mlir/IR/Value.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
-#include "llvm/IR/LLVMRemarkStreamer.h"
-#include "llvm/Remarks/RemarkFormat.h"
#include "llvm/Support/FileSystem.h"
using namespace mlir;
@@ -62,29 +59,65 @@ RemarkBase::RemarkKeyValue::RemarkKeyValue(StringRef key, unsigned long n)
RemarkBase::RemarkKeyValue::RemarkKeyValue(StringRef key, unsigned long long n)
: key(std::string(key)), val(llvm::utostr(n)) {}
-void RemarkBase::print(llvm::DiagnosticPrinter &dp) const {
- std::string str;
- llvm::raw_string_ostream os(str);
- getLocation()->print(os);
- os.flush();
- dp << str << ": " << getMsg();
+void RemarkBase::insert(StringRef s) { args.emplace_back(s); }
+
+void RemarkBase::insert(RemarkKeyValue a) { args.push_back(std::move(a)); }
+
+// Simple helper to print key=val list.
+static void printArgs(llvm::raw_ostream &os,
+ llvm::ArrayRef<RemarkBase::RemarkKeyValue> args) {
+ if (args.empty())
+ return;
+ os << " {\n";
+ for (size_t i = 0; i < args.size(); ++i) {
+ const auto &a = args[i];
+ os << a.key << "=" << a.val;
+ if (i + 1 < args.size())
+ os << ", ";
+ os << "\n";
+ }
+ os << "\n}";
}
-void RemarkBase::print() const { emitError(getLocation(), getMsg()); }
+void RemarkBase::print(llvm::raw_ostream &os, bool printLocation) const {
+ os << getRemarkTypeString() << "\n";
+ os << getPassName() << ":" << getRemarkName() << "\n";
-void RemarkBase::insert(StringRef s) { args.emplace_back(s); }
+ // Function (if any)
+ if (!getFunction().empty())
+ os << " func=" << getFunction() << "\n";
-void RemarkBase::insert(RemarkKeyValue a) { args.push_back(std::move(a)); }
+ if (printLocation)
+ if (auto flc = mlir::dyn_cast<mlir::FileLineColLoc>(getLocation()))
+ os << " @" << flc.getFilename() << ":" << flc.getLine() << ":"
+ << flc.getColumn();
-void RemarkBase::insert(SetIsVerbose v) { isVerboseRemark = true; }
+ // Key/Value args
+ printArgs(os, getArgs());
+}
std::string RemarkBase::getMsg() const {
- std::string str;
- llvm::raw_string_ostream rss(str);
- for (const RemarkBase::RemarkKeyValue &arg :
- llvm::make_range(args.begin(), args.end()))
- rss << arg.val;
- return rss.str();
+ std::string s;
+ llvm::raw_string_ostream os(s);
+ print(os);
+ os.flush();
+ return s;
+}
+
+std::string RemarkBase::getRemarkTypeString() const {
+ switch (remarkKind) {
+ case RemarkKind::OptimizationRemarkUnknown:
+ return "Unknown";
+ case RemarkKind::OptimizationRemarkPassed:
+ return "Passed";
+ case RemarkKind::OptimizationRemarkMissed:
+ return "Missed";
+ case RemarkKind::OptimizationRemarkFailure:
+ return "Failure";
+ case RemarkKind::OptimizationRemarkAnalysis:
+ return "Analysis";
+ }
+ llvm_unreachable("Unknown remark kind");
}
llvm::remarks::Type RemarkBase::getRemarkType() const {
@@ -200,92 +233,42 @@ RemarkEngine::emitOptimizationRemarkAnalysis(Location loc, StringRef passName,
loc, passName, categoryName, &RemarkEngine::isAnalysisOptRemarkEnabled);
}
-//===----------------------------------------------------------------------===//
-// RemarkStreamer
-//===----------------------------------------------------------------------===//
-void MLIRRemarkStreamer::streamOptimizationRemark(const RemarkBase &remark) {
- if (!remarkStreamer.matchesFilter(remark.getPassName()))
- return;
-
- // First, convert the diagnostic to a remark.
- llvm::remarks::Remark r = remark.generateRemark();
- // Then, emit the remark through the serializer.
- remarkStreamer.getSerializer().emit(r);
-}
//===----------------------------------------------------------------------===//
// Remarkengine
//===----------------------------------------------------------------------===//
-void RemarkEngine::report(const RemarkBase &&diag) {
+void RemarkEngine::report(const RemarkBase &&remark) {
// Stream the remark
- if (getLLVMRemarkStreamer() && remarksFile)
- getLLVMRemarkStreamer()->streamOptimizationRemark(diag);
+ if (remarkStreamer)
+ remarkStreamer->streamOptimizationRemark(remark);
// Print using MLIR's diagnostic
if (printAsEmitRemarks)
- emitRemark(diag.getLocation(), diag.getMsg());
+ emitRemark(remark.getLocation(), remark.getMsg());
}
RemarkEngine::~RemarkEngine() {
- if (remarksFile) {
- remarksFile->keep();
- remarksFile.reset();
- }
- setMainRemarkStreamer(nullptr);
- setRemarkStreamer(nullptr);
+ if (remarkStreamer)
+ remarkStreamer->finalize();
}
-LogicalResult RemarkEngine::initialize(StringRef outputPath,
- llvm::remarks::Format fmt,
- std::string *errMsg) {
- auto fail = [&](llvm::StringRef msg) {
- if (errMsg)
- *errMsg = msg.str();
- return failure();
- };
-
- if (remarksFile)
- return fail("RemarkEngine is already initialized with an output file.");
-
- llvm::sys::fs::OpenFlags flags = (fmt == llvm::remarks::Format::YAML)
- ? llvm::sys::fs::OF_Text
- : llvm::sys::fs::OF_None;
- std::error_code ec;
- remarksFile = std::make_unique<llvm::ToolOutputFile>(outputPath, ec, flags);
-
- if (ec) {
- remarksFile.reset();
- return fail(
- ("Failed to open remarks file '" + outputPath + "': " + ec.message())
- .str());
- }
-
- auto serializer = llvm::remarks::createRemarkSerializer(
- fmt, llvm::remarks::SerializerMode::Separate, remarksFile->os());
- if (!serializer) {
- remarksFile.reset();
- return fail(llvm::toString(serializer.takeError()));
- }
-
- setMainRemarkStreamer(std::make_unique<llvm::remarks::RemarkStreamer>(
- std::move(*serializer), outputPath));
- setRemarkStreamer(
- std::make_unique<MLIRRemarkStreamer>(*getMainRemarkStreamer()));
+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();
}
RemarkEngine::RemarkEngine(bool printAsEmitRemarks,
- std::optional<std::string> categoryPassName,
- std::optional<std::string> categoryMissName,
- std::optional<std::string> categoryAnalysisName,
- std::optional<std::string> categoryFailedName)
+ const MLIRContext::RemarkCategories &cats)
: printAsEmitRemarks(printAsEmitRemarks) {
- if (categoryPassName)
- passFilter = llvm::Regex(categoryPassName.value());
- if (categoryMissName)
- missFilter = llvm::Regex(categoryMissName.value());
- if (categoryAnalysisName)
- analysisFilter = llvm::Regex(categoryAnalysisName.value());
- if (categoryFailedName)
- failedFilter = llvm::Regex(categoryFailedName.value());
+ if (cats.passed)
+ passFilter = llvm::Regex(cats.passed.value());
+ if (cats.missed)
+ missFilter = llvm::Regex(cats.missed.value());
+ if (cats.analysis)
+ analysisFilter = llvm::Regex(cats.analysis.value());
+ if (cats.failed)
+ failedFilter = llvm::Regex(cats.failed.value());
}
diff --git a/mlir/lib/Remark/CMakeLists.txt b/mlir/lib/Remark/CMakeLists.txt
new file mode 100644
index 0000000000000..4888d71a7ad66
--- /dev/null
+++ b/mlir/lib/Remark/CMakeLists.txt
@@ -0,0 +1,14 @@
+add_mlir_library(MLIRRemarkStreamer
+ RemarkStreamer.cpp
+
+ ADDITIONAL_HEADER_DIRS
+ ${MLIR_MAIN_INCLUDE_DIR}/mlir/Remark
+
+ LINK_LIBS PUBLIC
+ MLIRIR
+
+ LINK_COMPONENTS
+ Remarks
+ Core
+ BitstreamReader
+ )
diff --git a/mlir/lib/Remark/RemarkStreamer.cpp b/mlir/lib/Remark/RemarkStreamer.cpp
new file mode 100644
index 0000000000000..be38b434dddc1
--- /dev/null
+++ b/mlir/lib/Remark/RemarkStreamer.cpp
@@ -0,0 +1,66 @@
+#include "mlir/Remark/RemarkStreamer.h"
+#include "mlir/IR/MLIRContext.h"
+#include "mlir/IR/Remarks.h"
+
+#include "llvm/Remarks/RemarkSerializer.h"
+#include "llvm/Remarks/RemarkStreamer.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/ToolOutputFile.h"
+
+namespace mlir::remark {
+
+FailureOr<std::unique_ptr<MLIRRemarkStreamerBase>>
+LLVMRemarkStreamer::createToFile(llvm::StringRef path,
+ llvm::remarks::Format fmt) {
+ std::error_code ec;
+ // Use error_code ctor; YAML is text. (Bitstream also works fine here.)
+ auto f =
+ std::make_unique<llvm::ToolOutputFile>(path, ec, llvm::sys::fs::OF_Text);
+ if (ec)
+ return failure();
+
+ auto serOr = llvm::remarks::createRemarkSerializer(
+ fmt, llvm::remarks::SerializerMode::Separate, f->os());
+ if (!serOr) {
+ llvm::consumeError(serOr.takeError());
+ return failure();
+ }
+
+ auto rs =
+ std::make_unique<llvm::remarks::RemarkStreamer>(std::move(*serOr), path);
+
+ auto impl = std::unique_ptr<LLVMRemarkStreamer>(new LLVMRemarkStreamer());
+ impl->remarkStreamer = std::move(rs);
+ impl->file = std::move(f);
+ return std::unique_ptr<MLIRRemarkStreamerBase>(std::move(impl));
+}
+
+void LLVMRemarkStreamer::streamOptimizationRemark(const RemarkBase &remark) {
+ if (!remarkStreamer->matchesFilter(remark.getPassName()))
+ return;
+
+ // First, convert the diagnostic to a remark.
+ llvm::remarks::Remark r = remark.generateRemark();
+ // Then, emit the remark through the serializer.
+ remarkStreamer->getSerializer().emit(r);
+}
+
+LLVMRemarkStreamer::~LLVMRemarkStreamer() {
+ if (file && remarkStreamer)
+ file->keep();
+}
+
+LogicalResult enableOptimizationRemarksToFile(
+ MLIRContext &ctx, StringRef path, llvm::remarks::Format fmt,
+ const MLIRContext::RemarkCategories &cat, bool printAsEmitRemarks) {
+
+ FailureOr<std::unique_ptr<MLIRRemarkStreamerBase>> sOr =
+ LLVMRemarkStreamer::createToFile(path, fmt);
+ if (failed(sOr))
+ return failure();
+ return ctx.enableOptimizationRemarks(std::move(*sOr), cat,
+ printAsEmitRemarks);
+}
+
+} // namespace mlir::remark
\ No newline at end of file
diff --git a/mlir/unittests/IR/CMakeLists.txt b/mlir/unittests/IR/CMakeLists.txt
index 07b242f7440bb..75cd2d65ef5a1 100644
--- a/mlir/unittests/IR/CMakeLists.txt
+++ b/mlir/unittests/IR/CMakeLists.txt
@@ -29,3 +29,4 @@ add_mlir_unittest(MLIRIRTests
target_include_directories(MLIRIRTests PRIVATE "${MLIR_BINARY_DIR}/test/lib/Dialect/Test")
mlir_target_link_libraries(MLIRIRTests PRIVATE MLIRIR)
target_link_libraries(MLIRIRTests PRIVATE MLIRTestDialect)
+target_link_libraries(MLIRIRTests PRIVATE MLIRRemarkStreamer)
diff --git a/mlir/unittests/IR/RemarkTest.cpp b/mlir/unittests/IR/RemarkTest.cpp
index 2472b140e07ac..9b2087ca4d4cf 100644
--- a/mlir/unittests/IR/RemarkTest.cpp
+++ b/mlir/unittests/IR/RemarkTest.cpp
@@ -7,11 +7,15 @@
//===----------------------------------------------------------------------===//
#include "mlir/IR/Diagnostics.h"
+#include "mlir/IR/MLIRContext.h"
#include "mlir/IR/Remarks.h"
+#include "mlir/Remark/RemarkStreamer.h"
#include "mlir/Support/TypeID.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/IR/LLVMRemarkStreamer.h"
#include "llvm/Remarks/RemarkFormat.h"
#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/LogicalResult.h"
#include "llvm/Support/YAMLParser.h"
#include "gtest/gtest.h"
#include <optional>
@@ -47,9 +51,14 @@ TEST(Remark, TestOutputOptimizationRemark) {
context.printStackTraceOnDiagnostic(true);
// Setup the remark engine
- context.setupOptimizationRemarks(yamlFile, llvm::remarks::Format::YAML,
- true, categoryLoopunroll, std::nullopt,
- std::nullopt, categoryLoopunroll);
+ mlir::MLIRContext::RemarkCategories cats{/*passed=*/categoryLoopunroll,
+ /*missed=*/std::nullopt,
+ /*analysis=*/std::nullopt,
+ /*failed=*/categoryLoopunroll};
+
+ LogicalResult isEnabled = mlir::remark::enableOptimizationRemarksToFile(
+ context, yamlFile, llvm::remarks::Format::YAML, cats);
+ ASSERT_TRUE(succeeded(isEnabled)) << "Failed to enable remark engine";
// Remark 1: pass, category LoopUnroll
reportOptimizationPass(loc, categoryLoopunroll, myPassname1) << pass1Msg;
@@ -118,13 +127,10 @@ TEST(Remark, TestOutputOptimizationRemarkDiagnostic) {
std::string categoryLoopunroll("LoopUnroll");
std::string myPassname1("myPass1");
std::string funcName("myFunc");
- SmallString<64> tmpPathStorage;
- sys::fs::createUniquePath("remarks-%%%%%%.yaml", tmpPathStorage,
- /*MakeAbsolute=*/true);
- std::string yamlFile =
- std::string(tmpPathStorage.data(), tmpPathStorage.size());
- ASSERT_FALSE(yamlFile.empty());
- std::string seenMsg;
+
+ std::string seenMsg = "";
+ std::string expectedMsg = "Passed\nLoopUnroll:myPass1\n func=<unknown "
+ "function>\n {\nString=My message\n\n}";
{
MLIRContext context;
Location loc = UnknownLoc::get(&context);
@@ -139,14 +145,83 @@ TEST(Remark, TestOutputOptimizationRemarkDiagnostic) {
});
// Setup the remark engine
- context.setupOptimizationRemarks(yamlFile, llvm::remarks::Format::YAML,
- true, categoryLoopunroll, std::nullopt,
- std::nullopt, categoryLoopunroll);
+ mlir::MLIRContext::RemarkCategories cats{/*passed=*/categoryLoopunroll,
+ /*missed=*/std::nullopt,
+ /*analysis=*/std::nullopt,
+ /*failed=*/categoryLoopunroll};
+
+ LogicalResult isEnabled =
+ context.enableOptimizationRemarks(nullptr, cats, true);
+ ASSERT_TRUE(succeeded(isEnabled)) << "Failed to enable remark engine";
// Remark 1: pass, category LoopUnroll
reportOptimizationPass(loc, categoryLoopunroll, myPassname1) << pass1Msg;
}
- EXPECT_EQ(seenMsg, pass1Msg);
+ EXPECT_EQ(seenMsg, expectedMsg);
}
+/// Custom remark streamer that prints remarks to stderr.
+class MyCustomStreamer : public MLIRRemarkStreamerBase {
+public:
+ MyCustomStreamer() = default;
+
+ void streamOptimizationRemark(const RemarkBase &remark) override {
+ llvm::errs() << "Custom remark: ";
+ remark.print(llvm::errs(), true);
+ llvm::errs() << "\n";
+ }
+};
+
+TEST(Remark, TestCustomOptimizationRemarkDiagnostic) {
+ testing::internal::CaptureStderr();
+ const auto *pass1Msg = "My message";
+ const auto *pass2Msg = "My another message";
+ const auto *pass3Msg = "Do not show this message";
+
+ std::string categoryLoopunroll("LoopUnroll");
+ std::string categoryInline("Inliner");
+ std::string myPassname1("myPass1");
+ std::string myPassname2("myPass2");
+ std::string funcName("myFunc");
+
+ std::string seenMsg = "";
+
+ {
+ MLIRContext context;
+ Location loc = UnknownLoc::get(&context);
+
+ // Setup the remark engine
+ mlir::MLIRContext::RemarkCategories cats{/*passed=*/categoryLoopunroll,
+ /*missed=*/std::nullopt,
+ /*analysis=*/std::nullopt,
+ /*failed=*/categoryLoopunroll};
+
+ LogicalResult isEnabled = context.enableOptimizationRemarks(
+ std::make_unique<MyCustomStreamer>(), cats, true);
+ ASSERT_TRUE(succeeded(isEnabled)) << "Failed to enable remark engine";
+
+ // Remark 1: pass, category LoopUnroll
+ reportOptimizationPass(loc, categoryLoopunroll, myPassname1) << pass1Msg;
+ // Remark 2: failure, category LoopUnroll
+ reportOptimizationFail(loc, categoryLoopunroll, myPassname2) << pass2Msg;
+ // Remark 3: pass, category Inline (should not be printed)
+ reportOptimizationPass(loc, categoryInline, myPassname1) << pass3Msg;
+ }
+
+ llvm::errs().flush();
+ std::string errOut = ::testing::internal::GetCapturedStderr();
+
+ // Expect exactly two "Custom remark:" lines.
+ auto first = errOut.find("Custom remark:");
+ EXPECT_NE(first, std::string::npos);
+ auto second = errOut.find("Custom remark:", first + 1);
+ EXPECT_NE(second, std::string::npos);
+ auto third = errOut.find("Custom remark:", second + 1);
+ EXPECT_EQ(third, std::string::npos);
+
+ // Containment checks for messages.
+ EXPECT_NE(errOut.find(pass1Msg), std::string::npos); // printed
+ EXPECT_NE(errOut.find(pass2Msg), std::string::npos); // printed
+ EXPECT_EQ(errOut.find(pass3Msg), std::string::npos); // filtered out
+}
} // namespace
>From 521c676919b75624fa549b698f1bb7cba159e935 Mon Sep 17 00:00:00 2001
From: Guray Ozen <gozen at nvidia.com>
Date: Mon, 11 Aug 2025 12:28:17 +0000
Subject: [PATCH 06/22] fix doc
---
mlir/docs/Remarks.md | 107 +++++++++++++++++++++++++++++++++----------
1 file changed, 83 insertions(+), 24 deletions(-)
diff --git a/mlir/docs/Remarks.md b/mlir/docs/Remarks.md
index 4885f66d00d05..8a92677e82b90 100644
--- a/mlir/docs/Remarks.md
+++ b/mlir/docs/Remarks.md
@@ -2,38 +2,75 @@
[TOC]
-Optimization remarks are structured, machine- and human-readable notes emitted by passes to explain what was optimized, what was missed, and why. MLIR integrates LLVM’s **remarks** infrastructure to make these insights easy to produce and consume.
+Remarks are structured, human- and machine-readable notes emitted by passes to
+explain what was optimized, what was missed, and why. The `RemarkEngine`
+collects finalized remarks during compilation and forwards them to a pluggable
+streamer. A default streamer integrates LLVM’s `llvm::remarks` so you can stream
+while a pass runs and serialize to disk (YAML or LLVM bitstream) for tooling.
**Key points**
-- **Opt-in**: Disabled by default. No cost unless enabled.
+- **Opt-in**: Disabled by default; zero overhead unless enabled.
- **Per-context**: Configured on `MLIRContext`.
-- **Formats**: YAML or LLVM remark bitstream.
-- **Kinds**: Pass, Missed, Failure, Analysis.
-- **API**: Lightweight stream interface (similar to diagnostics) with `<<`.
+- **Formats**: Custom streamers, or LLVM’s Remark engine (YAML / Bitstream).
+- **Kinds**: `Pass`, `Missed`, `Failure`, `Analysis`.
+- **API**: Lightweight streaming interface with `<<` (similar to diagnostics).
-## Enabling remarks
+## How it works
-Enable once per `MLIRContext` (e.g., in your tool, pass pipeline setup, or test):
+Remarks has two important classes:
+
+- **`RemarkEngine`** (owned by `MLIRContext`): receives finalized
+ `InFlightRemark`s, optionally mirrors them to the `DiagnosticEngine`, then
+ dispatches to the installed streamer.
+- **`MLIRRemarkStreamerBase`** (abstract): backend interface with a single hook
+ `streamOptimizationRemark(const RemarkBase &)`.
+
+**Default backend – `MLIRLLVMRemarkStreamer`** Adapts `mlir::RemarkBase` to
+`llvm::remarks::Remark` and writes YAML/bitstream via
+`llvm::remarks::RemarkStreamer` to a `ToolOutputFile`.
+
+**Ownership**: `MLIRContext` → `RemarkEngine` → `MLIRRemarkStreamerBase`.
+
+## Enable Remarks via mlir::emitRemarks (No Streamer)
+
+Enable once per `MLIRContext` (e.g., where you build your pass pipeline or in
+your tool). If `printAsEmitRemarks` is true, each remark is also mirrored to the
+context’s `DiagnosticEngine` under the provided category labels—handy for
+interactive tools and tests.
+
+```c++
+mlir::MLIRContext::RemarkCategories cats{/*passed=*/categoryLoopunroll,
+ /*missed=*/std::nullopt,
+ /*analysis=*/std::nullopt,
+ /*failed=*/categoryLoopunroll};
+
+context.enableOptimizationRemarks(/*streamer=*/nullptr,
+ cats,
+ /*printAsEmitRemarks=*/true);
+```
+
+## Enable Remarks with LLVMRemarkStreamer (YAML/Bitstream)
+
+If you want to persist remarks to a file in YAML or bitstream format, use
+`mlir::remark::LLVMRemarkStreamer` (helper shown below):
```c++
-#include "mlir/IR/MLIRContext.h"
-
-// Writes remarks to /tmp/remarks.yaml in YAML format and mirrors them to
-// the DiagnosticEngine as 'remark' diagnostics with the given category labels.
-context.setupOptimizationRemarks(
- /*outputPath=*/"/tmp/remarks.yaml",
- /*outputFormat=*/yaml, // or "bitstream"
- /*printAsEmitRemarks=*/true,
- /*categoryPassName=*/"opt.pass", // optional category labels for mirroring
- /*categoryMissName=*/"opt.missed",
- /*categoryAnalysisName=*/"opt.analysis",
- /*categoryFailedName=*/"opt.failed");
+#include "mlir/Remark/RemarkStreamer.h"
+
+mlir::MLIRContext::RemarkCategories cats{/*passed=*/categoryLoopunroll,
+ /*missed=*/std::nullopt,
+ /*analysis=*/std::nullopt,
+ /*failed=*/categoryLoopunroll};
+
+mlir::remark::enableOptimizationRemarksToFile(
+ context, yamlFile, llvm::remarks::Format::YAML, cats);
```
-### Emitting remarks from a pass
+## Emitting remarks from a pass
-The functions `reportOptimization*` return an in-flight remark object (like MLIR diagnostics). One can append strings and key–value pairs with <<.
+The `reportOptimization*` functions return an in-flight remark object (like MLIR
+diagnostics). Append strings and key–value pairs with `<<`.
```c++
#include "mlir/IR/Remarks.h"
@@ -68,11 +105,11 @@ LogicalResult MyPass::runOnOperation() {
}
```
-## Output formats
+### Output formats
#### YAML
-A typical remark serialized to YAML looks like following. It is Readable, easy to diff and grep.
+Readable, easy to diff and grep.
```yaml
--- !Passed
@@ -88,4 +125,26 @@ message: "vectorized loop with tripCount=128"
#### Bitstream
-Compact binary format supported by LLVM’s remark tooling. Prefer this for large production runs or where existing infrastructure already understands LLVM remarks.
+Compact binary format supported by LLVM’s remark tooling. Prefer this for large
+production runs or when existing infrastructure already consumes LLVM remarks.
+
+## Enable Remarks with a Custom Streamer
+
+`RemarkEngine` talks to `MLIRRemarkStreamerBase`. Implement your own streamer to
+consume remarks in any format you like:
+
+```c++
+class MyStreamer : public MLIRRemarkStreamerBase {
+public:
+ void streamOptimizationRemark(const RemarkBase &remark) override {
+ // Convert RemarkBase to your format and write it out.
+ }
+};
+
+// ...
+auto myStreamer = std::make_unique<MyStreamer>();
+context.setupOptimizationRemarks(path,
+ std::move(myStreamer),
+ /*printAsEmitRemarks=*/false,
+ /*categories=*/cat);
+```
>From d851e0e9821207b455a75ce96af533584ac48bab Mon Sep 17 00:00:00 2001
From: Guray Ozen <gozen at nvidia.com>
Date: Mon, 11 Aug 2025 14:54:34 +0000
Subject: [PATCH 07/22] add callback
---
mlir/docs/Remarks.md | 4 +++-
mlir/include/mlir/IR/Remarks.h | 8 ++++----
2 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/mlir/docs/Remarks.md b/mlir/docs/Remarks.md
index 8a92677e82b90..4b4498e7ea195 100644
--- a/mlir/docs/Remarks.md
+++ b/mlir/docs/Remarks.md
@@ -55,6 +55,8 @@ context.enableOptimizationRemarks(/*streamer=*/nullptr,
If you want to persist remarks to a file in YAML or bitstream format, use
`mlir::remark::LLVMRemarkStreamer` (helper shown below):
+You can read more information about [LLVM's Remark from here](https://llvm.org/docs/Remarks.html).
+
```c++
#include "mlir/Remark/RemarkStreamer.h"
@@ -92,7 +94,7 @@ LogicalResult MyPass::runOnOperation() {
// MISSED: explain why + suggest a fix
reportOptimizationMiss(loc, "unroll", "MyPass",
- /*suggestion=*/"increase unroll factor to >=4")
+ /*suggestion=*/[&](){ return "increase unroll factor to >=4"; })
<< "not profitable at this size";
// FAILURE: action attempted but failed
diff --git a/mlir/include/mlir/IR/Remarks.h b/mlir/include/mlir/IR/Remarks.h
index ccc2b79e1d05a..8ab4f037d21ef 100644
--- a/mlir/include/mlir/IR/Remarks.h
+++ b/mlir/include/mlir/IR/Remarks.h
@@ -362,13 +362,13 @@ inline InFlightRemark reportOptimizationPass(Location loc, StringRef cat,
}
/// Report an optimization remark that was missed.
-inline InFlightRemark reportOptimizationMiss(Location loc, StringRef cat,
- StringRef passName,
- StringRef suggestion) {
+inline InFlightRemark
+reportOptimizationMiss(Location loc, StringRef cat, StringRef passName,
+ llvm::function_ref<std::string()> makeSuggestion) {
auto r =
withEngine(&RemarkEngine::emitOptimizationRemarkMiss, loc, passName, cat);
if (r)
- r << suggest(suggestion);
+ r << suggest(makeSuggestion());
return r;
}
/// Report an optimization failure remark.
>From f4d7bd6433d817509f51a176e56fcab163766a56 Mon Sep 17 00:00:00 2001
From: Guray Ozen <gozen at nvidia.com>
Date: Mon, 11 Aug 2025 15:07:49 +0000
Subject: [PATCH 08/22] Add mlir::remark. RemarkBase->Remark
---
mlir/docs/Remarks.md | 14 +++----
mlir/include/mlir/IR/MLIRContext.h | 18 ++++----
mlir/include/mlir/IR/Remarks.h | 40 +++++++++---------
mlir/include/mlir/Remark/RemarkStreamer.h | 4 +-
mlir/lib/IR/MLIRContext.cpp | 12 +++---
mlir/lib/IR/Remarks.cpp | 51 +++++++++++------------
mlir/lib/Remark/RemarkStreamer.cpp | 4 +-
mlir/unittests/IR/RemarkTest.cpp | 2 +-
8 files changed, 73 insertions(+), 72 deletions(-)
diff --git a/mlir/docs/Remarks.md b/mlir/docs/Remarks.md
index 4b4498e7ea195..fe243ad30bcc5 100644
--- a/mlir/docs/Remarks.md
+++ b/mlir/docs/Remarks.md
@@ -24,9 +24,9 @@ Remarks has two important classes:
`InFlightRemark`s, optionally mirrors them to the `DiagnosticEngine`, then
dispatches to the installed streamer.
- **`MLIRRemarkStreamerBase`** (abstract): backend interface with a single hook
- `streamOptimizationRemark(const RemarkBase &)`.
+ `streamOptimizationRemark(const Remark &)`.
-**Default backend – `MLIRLLVMRemarkStreamer`** Adapts `mlir::RemarkBase` to
+**Default backend – `MLIRLLVMRemarkStreamer`** Adapts `mlir::Remark` to
`llvm::remarks::Remark` and writes YAML/bitstream via
`llvm::remarks::RemarkStreamer` to a `ToolOutputFile`.
@@ -85,12 +85,12 @@ LogicalResult MyPass::runOnOperation() {
// PASS: something succeeded
reportOptimizationPass(loc, /*category=*/"vectorizer", /*passName=*/"MyPass")
- << "vectorized loop with tripCount="
- << RemarkBase::RemarkKeyValue("tripCount", 128);
+ << "vectorized loop."
+ << Remark::RemarkKeyValue("tripCount", 128);
// ANALYSIS: neutral insight
reportOptimizationAnalysis(loc, "unroll", "MyPass")
- << "estimated cost: " << RemarkBase::RemarkKeyValue("cost", 42);
+ << "estimated cost: " << Remark::RemarkKeyValue("cost", 42);
// MISSED: explain why + suggest a fix
reportOptimizationMiss(loc, "unroll", "MyPass",
@@ -138,8 +138,8 @@ consume remarks in any format you like:
```c++
class MyStreamer : public MLIRRemarkStreamerBase {
public:
- void streamOptimizationRemark(const RemarkBase &remark) override {
- // Convert RemarkBase to your format and write it out.
+ void streamOptimizationRemark(const Remark &remark) override {
+ // Convert Remark to your format and write it out.
}
};
diff --git a/mlir/include/mlir/IR/MLIRContext.h b/mlir/include/mlir/IR/MLIRContext.h
index cfb408b1f13eb..87786420de37d 100644
--- a/mlir/include/mlir/IR/MLIRContext.h
+++ b/mlir/include/mlir/IR/MLIRContext.h
@@ -12,7 +12,6 @@
#include "mlir/Support/LLVM.h"
#include "mlir/Support/TypeID.h"
#include "llvm/ADT/ArrayRef.h"
-#include "llvm/Remarks/RemarkFormat.h"
#include <functional>
#include <memory>
#include <vector>
@@ -33,10 +32,12 @@ class InFlightDiagnostic;
class Location;
class MLIRContextImpl;
class RegisteredOperationName;
-class MLIRRemarkStreamerBase;
-class RemarkEngine;
class StorageUniquer;
class IRUnit;
+namespace remark {
+class MLIRRemarkStreamerBase;
+class RemarkEngine;
+} // namespace remark
/// MLIRContext is the top-level object for a collection of MLIR operations. It
/// holds immortal uniqued objects like types, and the tables used to unique
@@ -220,7 +221,7 @@ class MLIRContext {
DiagnosticEngine &getDiagEngine();
/// Returns the remark engine for this context.
- RemarkEngine *getRemarkEngine();
+ remark::RemarkEngine *getRemarkEngine();
/// Returns the storage uniquer used for creating affine constructs.
StorageUniquer &getAffineUniquer();
@@ -263,10 +264,9 @@ class MLIRContext {
/// emitted.
/// If `printAsEmitRemarks` is true, the remarks will be printed as
/// mlir::emitRemarks.
- LogicalResult
- enableOptimizationRemarks(std::unique_ptr<MLIRRemarkStreamerBase> streamer,
- const RemarkCategories &cats,
- bool printAsEmitRemarks = false);
+ LogicalResult enableOptimizationRemarks(
+ std::unique_ptr<remark::MLIRRemarkStreamerBase> streamer,
+ const RemarkCategories &cats, bool printAsEmitRemarks = false);
//===--------------------------------------------------------------------===//
// Action API
@@ -305,7 +305,7 @@ class MLIRContext {
private:
/// Set the remark engine for this context.
- void setRemarkEngine(std::unique_ptr<RemarkEngine> engine);
+ void setRemarkEngine(std::unique_ptr<remark::RemarkEngine> engine);
/// Return true if the given dialect is currently loading.
bool isDialectLoading(StringRef dialectNamespace);
diff --git a/mlir/include/mlir/IR/Remarks.h b/mlir/include/mlir/IR/Remarks.h
index 8ab4f037d21ef..7f8e10918dc04 100644
--- a/mlir/include/mlir/IR/Remarks.h
+++ b/mlir/include/mlir/IR/Remarks.h
@@ -16,12 +16,11 @@
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/Remarks/Remark.h"
#include "llvm/Support/Regex.h"
-#include "llvm/Support/ToolOutputFile.h"
#include "mlir/IR/Diagnostics.h"
#include "mlir/IR/MLIRContext.h"
-namespace mlir {
+namespace mlir::remark {
/// Defines different remark kinds that can be used to categorize remarks.
enum class RemarkKind {
@@ -35,12 +34,12 @@ enum class RemarkKind {
//===----------------------------------------------------------------------===//
// Remark Base Class
//===----------------------------------------------------------------------===//
-class RemarkBase {
+class Remark {
public:
- RemarkBase(RemarkKind remarkKind, DiagnosticSeverity severity,
- const char *passName, StringRef remarkName, Location loc,
- std::optional<StringRef> functionName = std::nullopt)
+ Remark(RemarkKind remarkKind, DiagnosticSeverity severity,
+ const char *passName, StringRef remarkName, Location loc,
+ std::optional<StringRef> functionName = std::nullopt)
: remarkKind(remarkKind), functionName(functionName), loc(loc),
passName(passName), remarkName(remarkName) {}
@@ -142,16 +141,15 @@ class RemarkBase {
}
};
-inline RemarkBase &operator<<(RemarkBase &r, StringRef s) {
+inline Remark &operator<<(Remark &r, StringRef s) {
r.insert(s);
return r;
}
-inline RemarkBase &&operator<<(RemarkBase &&r, StringRef s) {
+inline Remark &&operator<<(Remark &&r, StringRef s) {
r.insert(s);
return std::move(r);
}
-inline RemarkBase &operator<<(RemarkBase &r,
- const RemarkBase::RemarkKeyValue &kv) {
+inline Remark &operator<<(Remark &r, const Remark::RemarkKeyValue &kv) {
r.insert(kv);
return r;
}
@@ -161,11 +159,11 @@ inline RemarkBase &operator<<(RemarkBase &r,
//===----------------------------------------------------------------------===//
template <RemarkKind K, DiagnosticSeverity S>
-class OptRemarkBase final : public RemarkBase {
+class OptRemarkBase final : public Remark {
public:
explicit OptRemarkBase(Location loc, StringRef passName,
StringRef categoryName)
- : RemarkBase(K, S, passName.data(), categoryName, loc) {}
+ : Remark(K, S, passName.data(), categoryName, loc) {}
};
using OptRemarkAnalysis = OptRemarkBase<RemarkKind::OptimizationRemarkAnalysis,
@@ -186,15 +184,15 @@ class RemarkEngine;
// InFlightRemark
//===----------------------------------------------------------------------===//
-/// InFlightRemark is a RAII class that holds a reference to a RemarkBase
+/// InFlightRemark is a RAII class that holds a reference to a Remark
/// instance and allows to build the remark using the << operator. The remark
/// is emitted when the InFlightRemark instance is destroyed, which happens
/// when the scope ends or when the InFlightRemark instance is moved.
/// Similar to InFlightDiagnostic, but for remarks.
class InFlightRemark {
public:
- explicit InFlightRemark(RemarkBase *diag) : remark(diag) {}
- InFlightRemark(RemarkEngine &eng, std::unique_ptr<RemarkBase> diag)
+ explicit InFlightRemark(Remark *diag) : remark(diag) {}
+ InFlightRemark(RemarkEngine &eng, std::unique_ptr<Remark> diag)
: owner(&eng), remark(std::move(diag)) {}
InFlightRemark() = default; // empty ctor
@@ -217,7 +215,7 @@ class InFlightRemark {
private:
RemarkEngine *owner{nullptr};
- std::unique_ptr<RemarkBase> remark;
+ std::unique_ptr<Remark> remark;
};
//===----------------------------------------------------------------------===//
@@ -236,7 +234,7 @@ class MLIRRemarkStreamerBase {
///
/// It must be overridden by the derived classes to provide
/// the actual streaming implementation.
- virtual void streamOptimizationRemark(const RemarkBase &remark) = 0;
+ virtual void streamOptimizationRemark(const Remark &remark) = 0;
virtual void finalize() {} // optional
};
@@ -286,7 +284,7 @@ class RemarkEngine {
}
/// Emit a remark using the given maker function, which should return
- /// a RemarkBase instance. The remark will be emitted using the main
+ /// a Remark instance. The remark will be emitted using the main
/// remark streamer.
template <typename RemarkT, typename... Args>
InFlightRemark makeRemark(Args &&...args);
@@ -315,7 +313,7 @@ class RemarkEngine {
std::string *errMsg);
/// Report a remark.
- void report(const RemarkBase &&remark);
+ void report(const Remark &&remark);
/// Report a successful remark, this will create an InFlightRemark
/// that can be used to build the remark using the << operator.
@@ -340,7 +338,7 @@ class RemarkEngine {
// Emitters
//===----------------------------------------------------------------------===//
-using Suggestion = RemarkBase::RemarkKeyValue;
+using Suggestion = Remark::RemarkKeyValue;
inline Suggestion suggest(StringRef txt) { return {"Suggestion", txt}; }
template <typename Fn, typename... Args>
@@ -385,6 +383,6 @@ inline InFlightRemark reportOptimizationAnalysis(Location loc, StringRef cat,
passName, cat);
}
-} // namespace mlir
+} // namespace mlir::remark
#endif // MLIR_IR_REMARKS_H
\ No newline at end of file
diff --git a/mlir/include/mlir/Remark/RemarkStreamer.h b/mlir/include/mlir/Remark/RemarkStreamer.h
index 649e8028fe737..d985795ea4280 100644
--- a/mlir/include/mlir/Remark/RemarkStreamer.h
+++ b/mlir/include/mlir/Remark/RemarkStreamer.h
@@ -1,5 +1,7 @@
#include "mlir/IR/Remarks.h"
+
#include "llvm/Remarks/RemarkStreamer.h"
+#include "llvm/Support/ToolOutputFile.h"
using namespace llvm;
namespace mlir::remark {
@@ -11,7 +13,7 @@ class LLVMRemarkStreamer final : public MLIRRemarkStreamerBase {
static FailureOr<std::unique_ptr<MLIRRemarkStreamerBase>>
createToFile(llvm::StringRef path, llvm::remarks::Format fmt);
- void streamOptimizationRemark(const RemarkBase &remark) override;
+ void streamOptimizationRemark(const Remark &remark) override;
void finalize() override {}
~LLVMRemarkStreamer() override;
diff --git a/mlir/lib/IR/MLIRContext.cpp b/mlir/lib/IR/MLIRContext.cpp
index e71db084c4b81..55cbac4b2f570 100644
--- a/mlir/lib/IR/MLIRContext.cpp
+++ b/mlir/lib/IR/MLIRContext.cpp
@@ -138,7 +138,7 @@ class MLIRContextImpl {
//===--------------------------------------------------------------------===//
// Remark
//===--------------------------------------------------------------------===//
- std::unique_ptr<RemarkEngine> remarkEngine;
+ std::unique_ptr<remark::RemarkEngine> remarkEngine;
//===--------------------------------------------------------------------===//
// Options
@@ -399,18 +399,20 @@ DiagnosticEngine &MLIRContext::getDiagEngine() { return getImpl().diagEngine; }
//===----------------------------------------------------------------------===//
/// Returns the remark engine for this context.
-void MLIRContext::setRemarkEngine(std::unique_ptr<RemarkEngine> engine) {
+void MLIRContext::setRemarkEngine(
+ std::unique_ptr<remark::RemarkEngine> engine) {
getImpl().remarkEngine = std::move(engine);
}
-RemarkEngine *MLIRContext::getRemarkEngine() {
+remark::RemarkEngine *MLIRContext::getRemarkEngine() {
return getImpl().remarkEngine.get();
}
LogicalResult MLIRContext::enableOptimizationRemarks(
- std::unique_ptr<MLIRRemarkStreamerBase> streamer,
+ std::unique_ptr<remark::MLIRRemarkStreamerBase> streamer,
const RemarkCategories &cats, bool printAsEmitRemarks) {
- auto engine = std::make_unique<RemarkEngine>(printAsEmitRemarks, cats);
+ auto engine =
+ std::make_unique<remark::RemarkEngine>(printAsEmitRemarks, cats);
std::string errMsg;
if (failed(engine->initialize(std::move(streamer), &errMsg))) {
diff --git a/mlir/lib/IR/Remarks.cpp b/mlir/lib/IR/Remarks.cpp
index 6090682f72138..6360960b851a1 100644
--- a/mlir/lib/IR/Remarks.cpp
+++ b/mlir/lib/IR/Remarks.cpp
@@ -14,58 +14,57 @@
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
-#include "llvm/Support/FileSystem.h"
-using namespace mlir;
+using namespace mlir::remark;
//------------------------------------------------------------------------------
-// RemarkBase
+// Remark
//------------------------------------------------------------------------------
-RemarkBase::RemarkKeyValue::RemarkKeyValue(StringRef key, Value value)
+Remark::RemarkKeyValue::RemarkKeyValue(StringRef key, Value value)
: key(std::string(key)) {
llvm::raw_string_ostream rss(val);
rss << value;
}
-RemarkBase::RemarkKeyValue::RemarkKeyValue(StringRef key, Type type)
+Remark::RemarkKeyValue::RemarkKeyValue(StringRef key, Type type)
: key(std::string(key)) {
llvm::raw_string_ostream os(val);
os << type;
}
-RemarkBase::RemarkKeyValue::RemarkKeyValue(StringRef key, StringRef s)
+Remark::RemarkKeyValue::RemarkKeyValue(StringRef key, StringRef s)
: key(std::string(key)), val(s.str()) {}
-RemarkBase::RemarkKeyValue::RemarkKeyValue(StringRef key, int n)
+Remark::RemarkKeyValue::RemarkKeyValue(StringRef key, int n)
: key(std::string(key)), val(llvm::itostr(n)) {}
-RemarkBase::RemarkKeyValue::RemarkKeyValue(StringRef key, float n)
+Remark::RemarkKeyValue::RemarkKeyValue(StringRef key, float n)
: key(std::string(key)), val(std::to_string(n)) {}
-RemarkBase::RemarkKeyValue::RemarkKeyValue(StringRef key, long n)
+Remark::RemarkKeyValue::RemarkKeyValue(StringRef key, long n)
: key(std::string(key)), val(llvm::itostr(n)) {}
-RemarkBase::RemarkKeyValue::RemarkKeyValue(StringRef key, long long n)
+Remark::RemarkKeyValue::RemarkKeyValue(StringRef key, long long n)
: key(std::string(key)), val(llvm::itostr(n)) {}
-RemarkBase::RemarkKeyValue::RemarkKeyValue(StringRef key, unsigned n)
+Remark::RemarkKeyValue::RemarkKeyValue(StringRef key, unsigned n)
: key(std::string(key)), val(llvm::utostr(n)) {}
-RemarkBase::RemarkKeyValue::RemarkKeyValue(StringRef key, unsigned long n)
+Remark::RemarkKeyValue::RemarkKeyValue(StringRef key, unsigned long n)
: key(std::string(key)), val(llvm::utostr(n)) {}
-RemarkBase::RemarkKeyValue::RemarkKeyValue(StringRef key, unsigned long long n)
+Remark::RemarkKeyValue::RemarkKeyValue(StringRef key, unsigned long long n)
: key(std::string(key)), val(llvm::utostr(n)) {}
-void RemarkBase::insert(StringRef s) { args.emplace_back(s); }
+void Remark::insert(StringRef s) { args.emplace_back(s); }
-void RemarkBase::insert(RemarkKeyValue a) { args.push_back(std::move(a)); }
+void Remark::insert(RemarkKeyValue a) { args.push_back(std::move(a)); }
// Simple helper to print key=val list.
static void printArgs(llvm::raw_ostream &os,
- llvm::ArrayRef<RemarkBase::RemarkKeyValue> args) {
+ llvm::ArrayRef<Remark::RemarkKeyValue> args) {
if (args.empty())
return;
os << " {\n";
@@ -79,7 +78,7 @@ static void printArgs(llvm::raw_ostream &os,
os << "\n}";
}
-void RemarkBase::print(llvm::raw_ostream &os, bool printLocation) const {
+void Remark::print(llvm::raw_ostream &os, bool printLocation) const {
os << getRemarkTypeString() << "\n";
os << getPassName() << ":" << getRemarkName() << "\n";
@@ -96,7 +95,7 @@ void RemarkBase::print(llvm::raw_ostream &os, bool printLocation) const {
printArgs(os, getArgs());
}
-std::string RemarkBase::getMsg() const {
+std::string Remark::getMsg() const {
std::string s;
llvm::raw_string_ostream os(s);
print(os);
@@ -104,7 +103,7 @@ std::string RemarkBase::getMsg() const {
return s;
}
-std::string RemarkBase::getRemarkTypeString() const {
+std::string Remark::getRemarkTypeString() const {
switch (remarkKind) {
case RemarkKind::OptimizationRemarkUnknown:
return "Unknown";
@@ -120,7 +119,7 @@ std::string RemarkBase::getRemarkTypeString() const {
llvm_unreachable("Unknown remark kind");
}
-llvm::remarks::Type RemarkBase::getRemarkType() const {
+llvm::remarks::Type Remark::getRemarkType() const {
switch (remarkKind) {
case RemarkKind::OptimizationRemarkUnknown:
return llvm::remarks::Type::Unknown;
@@ -136,7 +135,7 @@ llvm::remarks::Type RemarkBase::getRemarkType() const {
llvm_unreachable("Unknown remark kind");
}
-llvm::remarks::Remark RemarkBase::generateRemark() const {
+llvm::remarks::Remark Remark::generateRemark() const {
auto locLambda = [&]() -> llvm::remarks::RemarkLocation {
if (auto flc = dyn_cast<FileLineColLoc>(getLocation()))
return {flc.getFilename(), flc.getLine(), flc.getColumn()};
@@ -149,7 +148,7 @@ llvm::remarks::Remark RemarkBase::generateRemark() const {
r.RemarkName = getRemarkName();
r.FunctionName = getFunction();
r.Loc = locLambda();
- for (const RemarkBase::RemarkKeyValue &arg : getArgs()) {
+ for (const Remark::RemarkKeyValue &arg : getArgs()) {
r.Args.emplace_back();
r.Args.back().Key = arg.key;
r.Args.back().Val = arg.val;
@@ -173,8 +172,8 @@ InFlightRemark::~InFlightRemark() {
template <typename RemarkT, typename... Args>
InFlightRemark RemarkEngine::makeRemark(Args &&...args) {
- static_assert(std::is_base_of_v<RemarkBase, RemarkT>,
- "RemarkT must derive from RemarkBase");
+ static_assert(std::is_base_of_v<Remark, RemarkT>,
+ "RemarkT must derive from Remark");
return InFlightRemark(*this,
std::make_unique<RemarkT>(std::forward<Args>(args)...));
}
@@ -237,7 +236,7 @@ RemarkEngine::emitOptimizationRemarkAnalysis(Location loc, StringRef passName,
// Remarkengine
//===----------------------------------------------------------------------===//
-void RemarkEngine::report(const RemarkBase &&remark) {
+void RemarkEngine::report(const Remark &&remark) {
// Stream the remark
if (remarkStreamer)
remarkStreamer->streamOptimizationRemark(remark);
@@ -252,7 +251,7 @@ RemarkEngine::~RemarkEngine() {
remarkStreamer->finalize();
}
-LogicalResult
+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.
diff --git a/mlir/lib/Remark/RemarkStreamer.cpp b/mlir/lib/Remark/RemarkStreamer.cpp
index be38b434dddc1..c58d8c71531fa 100644
--- a/mlir/lib/Remark/RemarkStreamer.cpp
+++ b/mlir/lib/Remark/RemarkStreamer.cpp
@@ -33,10 +33,10 @@ LLVMRemarkStreamer::createToFile(llvm::StringRef path,
auto impl = std::unique_ptr<LLVMRemarkStreamer>(new LLVMRemarkStreamer());
impl->remarkStreamer = std::move(rs);
impl->file = std::move(f);
- return std::unique_ptr<MLIRRemarkStreamerBase>(std::move(impl));
+ return std::unique_ptr<mlir::remark::MLIRRemarkStreamerBase>(std::move(impl));
}
-void LLVMRemarkStreamer::streamOptimizationRemark(const RemarkBase &remark) {
+void LLVMRemarkStreamer::streamOptimizationRemark(const Remark &remark) {
if (!remarkStreamer->matchesFilter(remark.getPassName()))
return;
diff --git a/mlir/unittests/IR/RemarkTest.cpp b/mlir/unittests/IR/RemarkTest.cpp
index 9b2087ca4d4cf..898a92a9bf29d 100644
--- a/mlir/unittests/IR/RemarkTest.cpp
+++ b/mlir/unittests/IR/RemarkTest.cpp
@@ -165,7 +165,7 @@ class MyCustomStreamer : public MLIRRemarkStreamerBase {
public:
MyCustomStreamer() = default;
- void streamOptimizationRemark(const RemarkBase &remark) override {
+ void streamOptimizationRemark(const Remark &remark) override {
llvm::errs() << "Custom remark: ";
remark.print(llvm::errs(), true);
llvm::errs() << "\n";
>From 514d429f54ae4476d79742da56143a503cd81614 Mon Sep 17 00:00:00 2001
From: Guray Ozen <gozen at nvidia.com>
Date: Tue, 12 Aug 2025 09:13:03 +0000
Subject: [PATCH 09/22] Pass->Passed
---
mlir/docs/Remarks.md | 12 +++++-----
mlir/include/mlir/IR/Remarks.h | 29 +++++++++++++----------
mlir/include/mlir/Remark/RemarkStreamer.h | 2 +-
mlir/lib/IR/Remarks.cpp | 21 ++++++++--------
mlir/lib/Remark/RemarkStreamer.cpp | 2 +-
mlir/unittests/IR/RemarkTest.cpp | 13 +++++-----
6 files changed, 42 insertions(+), 37 deletions(-)
diff --git a/mlir/docs/Remarks.md b/mlir/docs/Remarks.md
index fe243ad30bcc5..adbcc44de9d12 100644
--- a/mlir/docs/Remarks.md
+++ b/mlir/docs/Remarks.md
@@ -2,8 +2,8 @@
[TOC]
-Remarks are structured, human- and machine-readable notes emitted by passes to
-explain what was optimized, what was missed, and why. The `RemarkEngine`
+Remarks are structured, human- and machine-readable notes emitted by compiler to
+explain what was transformed, what was missed, and why. The `RemarkEngine`
collects finalized remarks during compilation and forwards them to a pluggable
streamer. A default streamer integrates LLVM’s `llvm::remarks` so you can stream
while a pass runs and serialize to disk (YAML or LLVM bitstream) for tooling.
@@ -13,7 +13,7 @@ while a pass runs and serialize to disk (YAML or LLVM bitstream) for tooling.
- **Opt-in**: Disabled by default; zero overhead unless enabled.
- **Per-context**: Configured on `MLIRContext`.
- **Formats**: Custom streamers, or LLVM’s Remark engine (YAML / Bitstream).
-- **Kinds**: `Pass`, `Missed`, `Failure`, `Analysis`.
+- **Kinds**: `Passed`, `Missed`, `Failure`, `Analysis`.
- **API**: Lightweight streaming interface with `<<` (similar to diagnostics).
## How it works
@@ -84,13 +84,13 @@ LogicalResult MyPass::runOnOperation() {
Location loc = op->getLoc();
// PASS: something succeeded
- reportOptimizationPass(loc, /*category=*/"vectorizer", /*passName=*/"MyPass")
+ reportRemarkPassed(loc, /*category=*/"vectorizer", /*passName=*/"MyPass")
<< "vectorized loop."
<< Remark::RemarkKeyValue("tripCount", 128);
// ANALYSIS: neutral insight
- reportOptimizationAnalysis(loc, "unroll", "MyPass")
- << "estimated cost: " << Remark::RemarkKeyValue("cost", 42);
+ reportOptimizationAnalysis(loc, "RegisterCount", "")
+ << "Kernel uses 168 registers"
// MISSED: explain why + suggest a fix
reportOptimizationMiss(loc, "unroll", "MyPass",
diff --git a/mlir/include/mlir/IR/Remarks.h b/mlir/include/mlir/IR/Remarks.h
index 7f8e10918dc04..563a57f8d725c 100644
--- a/mlir/include/mlir/IR/Remarks.h
+++ b/mlir/include/mlir/IR/Remarks.h
@@ -25,10 +25,10 @@ namespace mlir::remark {
/// Defines different remark kinds that can be used to categorize remarks.
enum class RemarkKind {
OptimizationRemarkUnknown = 0,
- OptimizationRemarkPassed,
- OptimizationRemarkMissed,
- OptimizationRemarkFailure,
- OptimizationRemarkAnalysis,
+ OptimizationRemarkPassed, // Optimization remark that was passed.
+ OptimizationRemarkMissed, // Optimization remark that was missed.
+ OptimizationRemarkFailure, // Optimization failure remark.
+ OptimizationRemarkAnalysis, // Analysis remark that does not indicate a pass.
};
//===----------------------------------------------------------------------===//
@@ -334,15 +334,18 @@ class RemarkEngine {
StringRef category);
};
+using RemarkKV = remark::Remark::RemarkKeyValue;
+
+} // namespace mlir::remark
+
//===----------------------------------------------------------------------===//
// Emitters
//===----------------------------------------------------------------------===//
-
-using Suggestion = Remark::RemarkKeyValue;
-inline Suggestion suggest(StringRef txt) { return {"Suggestion", txt}; }
+namespace mlir {
+using namespace mlir::remark;
template <typename Fn, typename... Args>
-inline InFlightRemark withEngine(Fn fn, Location loc, Args &&...args) {
+inline remark::InFlightRemark withEngine(Fn fn, Location loc, Args &&...args) {
MLIRContext *ctx = loc->getContext();
RemarkEngine *enginePtr = ctx->getRemarkEngine();
@@ -354,11 +357,13 @@ inline InFlightRemark withEngine(Fn fn, Location loc, Args &&...args) {
}
/// Report an optimization remark that was passed.
-inline InFlightRemark reportOptimizationPass(Location loc, StringRef cat,
- StringRef passName) {
+inline InFlightRemark reportRemarkPassed(Location loc, StringRef cat,
+ StringRef passName) {
return withEngine(&RemarkEngine::emitOptimizationRemark, loc, passName, cat);
}
+inline RemarkKV suggest(StringRef s) { return {"Suggestion", s}; }
+
/// Report an optimization remark that was missed.
inline InFlightRemark
reportOptimizationMiss(Location loc, StringRef cat, StringRef passName,
@@ -383,6 +388,6 @@ inline InFlightRemark reportOptimizationAnalysis(Location loc, StringRef cat,
passName, cat);
}
-} // namespace mlir::remark
+} // namespace mlir
-#endif // MLIR_IR_REMARKS_H
\ No newline at end of file
+#endif // MLIR_IR_REMARKS_H
diff --git a/mlir/include/mlir/Remark/RemarkStreamer.h b/mlir/include/mlir/Remark/RemarkStreamer.h
index d985795ea4280..3c055442bffd6 100644
--- a/mlir/include/mlir/Remark/RemarkStreamer.h
+++ b/mlir/include/mlir/Remark/RemarkStreamer.h
@@ -32,4 +32,4 @@ LogicalResult enableOptimizationRemarksToFile(
MLIRContext &ctx, StringRef path, llvm::remarks::Format fmt,
const MLIRContext::RemarkCategories &cat, bool printAsEmitRemarks = false);
-} // namespace mlir::remark
\ No newline at end of file
+} // namespace mlir::remark
diff --git a/mlir/lib/IR/Remarks.cpp b/mlir/lib/IR/Remarks.cpp
index 6360960b851a1..3dac417037308 100644
--- a/mlir/lib/IR/Remarks.cpp
+++ b/mlir/lib/IR/Remarks.cpp
@@ -1,4 +1,4 @@
-//===- Remarks.cpp - MLIR Remarks ---------------------------------===//
+//===- Remarks.cpp - MLIR Remarks -----------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -67,24 +67,25 @@ static void printArgs(llvm::raw_ostream &os,
llvm::ArrayRef<Remark::RemarkKeyValue> args) {
if (args.empty())
return;
- os << " {\n";
+ os << " {";
for (size_t i = 0; i < args.size(); ++i) {
const auto &a = args[i];
os << a.key << "=" << a.val;
if (i + 1 < args.size())
os << ", ";
- os << "\n";
}
- os << "\n}";
+ os << "}";
}
+/// Print the remark to the given output stream.
+/// Example output:
+/// [Missed] LoopUnroll:UnrolledLoop func=myFunction @file.cpp:42:7
+/// {tripCount=128, reason=too_small}
void Remark::print(llvm::raw_ostream &os, bool printLocation) const {
- os << getRemarkTypeString() << "\n";
- os << getPassName() << ":" << getRemarkName() << "\n";
-
- // Function (if any)
- if (!getFunction().empty())
- os << " func=" << getFunction() << "\n";
+ os << '[' << getRemarkTypeString() << "] ";
+ os << getPassName() << ':' << getRemarkName();
+ if (functionName)
+ os << " func=" << getFunction() << " ";
if (printLocation)
if (auto flc = mlir::dyn_cast<mlir::FileLineColLoc>(getLocation()))
diff --git a/mlir/lib/Remark/RemarkStreamer.cpp b/mlir/lib/Remark/RemarkStreamer.cpp
index c58d8c71531fa..b6b4f8bc660c3 100644
--- a/mlir/lib/Remark/RemarkStreamer.cpp
+++ b/mlir/lib/Remark/RemarkStreamer.cpp
@@ -63,4 +63,4 @@ LogicalResult enableOptimizationRemarksToFile(
printAsEmitRemarks);
}
-} // namespace mlir::remark
\ No newline at end of file
+} // namespace mlir::remark
diff --git a/mlir/unittests/IR/RemarkTest.cpp b/mlir/unittests/IR/RemarkTest.cpp
index 898a92a9bf29d..ba4981260b137 100644
--- a/mlir/unittests/IR/RemarkTest.cpp
+++ b/mlir/unittests/IR/RemarkTest.cpp
@@ -61,11 +61,11 @@ TEST(Remark, TestOutputOptimizationRemark) {
ASSERT_TRUE(succeeded(isEnabled)) << "Failed to enable remark engine";
// Remark 1: pass, category LoopUnroll
- reportOptimizationPass(loc, categoryLoopunroll, myPassname1) << pass1Msg;
+ reportRemarkPassed(loc, categoryLoopunroll, myPassname1) << pass1Msg;
// Remark 2: failure, category LoopUnroll
reportOptimizationFail(loc, categoryLoopunroll, myPassname2) << pass2Msg;
// Remark 3: pass, category Inline (should not be printed)
- reportOptimizationPass(loc, categoryInline, myPassname1) << pass3Msg;
+ reportRemarkPassed(loc, categoryInline, myPassname1) << pass3Msg;
}
// Read the file
@@ -129,8 +129,7 @@ TEST(Remark, TestOutputOptimizationRemarkDiagnostic) {
std::string funcName("myFunc");
std::string seenMsg = "";
- std::string expectedMsg = "Passed\nLoopUnroll:myPass1\n func=<unknown "
- "function>\n {\nString=My message\n\n}";
+ std::string expectedMsg = "[Passed] LoopUnroll:myPass1 {String=My message}";
{
MLIRContext context;
Location loc = UnknownLoc::get(&context);
@@ -155,7 +154,7 @@ TEST(Remark, TestOutputOptimizationRemarkDiagnostic) {
ASSERT_TRUE(succeeded(isEnabled)) << "Failed to enable remark engine";
// Remark 1: pass, category LoopUnroll
- reportOptimizationPass(loc, categoryLoopunroll, myPassname1) << pass1Msg;
+ reportRemarkPassed(loc, categoryLoopunroll, myPassname1) << pass1Msg;
}
EXPECT_EQ(seenMsg, expectedMsg);
}
@@ -201,11 +200,11 @@ TEST(Remark, TestCustomOptimizationRemarkDiagnostic) {
ASSERT_TRUE(succeeded(isEnabled)) << "Failed to enable remark engine";
// Remark 1: pass, category LoopUnroll
- reportOptimizationPass(loc, categoryLoopunroll, myPassname1) << pass1Msg;
+ reportRemarkPassed(loc, categoryLoopunroll, myPassname1) << pass1Msg;
// Remark 2: failure, category LoopUnroll
reportOptimizationFail(loc, categoryLoopunroll, myPassname2) << pass2Msg;
// Remark 3: pass, category Inline (should not be printed)
- reportOptimizationPass(loc, categoryInline, myPassname1) << pass3Msg;
+ reportRemarkPassed(loc, categoryInline, myPassname1) << pass3Msg;
}
llvm::errs().flush();
>From 6e9b73482db98e76c0ab1829d0a054678274d23b Mon Sep 17 00:00:00 2001
From: Guray Ozen <gozen at nvidia.com>
Date: Tue, 12 Aug 2025 15:17:36 +0000
Subject: [PATCH 10/22] Add detail. Add Arg.
---
mlir/docs/Remarks.md | 76 ++++++++---
mlir/include/mlir/IR/MLIRContext.h | 10 +-
mlir/include/mlir/IR/Remarks.h | 153 ++++++++++++++--------
mlir/include/mlir/Remark/RemarkStreamer.h | 18 ++-
mlir/lib/IR/MLIRContext.cpp | 10 +-
mlir/lib/IR/Remarks.cpp | 49 ++-----
mlir/lib/Remark/RemarkStreamer.cpp | 10 +-
mlir/unittests/IR/RemarkTest.cpp | 126 ++++++++++--------
8 files changed, 267 insertions(+), 185 deletions(-)
diff --git a/mlir/docs/Remarks.md b/mlir/docs/Remarks.md
index adbcc44de9d12..8bf28a1f19b22 100644
--- a/mlir/docs/Remarks.md
+++ b/mlir/docs/Remarks.md
@@ -12,7 +12,7 @@ while a pass runs and serialize to disk (YAML or LLVM bitstream) for tooling.
- **Opt-in**: Disabled by default; zero overhead unless enabled.
- **Per-context**: Configured on `MLIRContext`.
-- **Formats**: Custom streamers, or LLVM’s Remark engine (YAML / Bitstream).
+- **Formats**: LLVM’s Remark engine (YAML / Bitstream), or pluggable custom streamers.
- **Kinds**: `Passed`, `Missed`, `Failure`, `Analysis`.
- **API**: Lightweight streaming interface with `<<` (similar to diagnostics).
@@ -27,11 +27,50 @@ Remarks has two important classes:
`streamOptimizationRemark(const Remark &)`.
**Default backend – `MLIRLLVMRemarkStreamer`** Adapts `mlir::Remark` to
-`llvm::remarks::Remark` and writes YAML/bitstream via
-`llvm::remarks::RemarkStreamer` to a `ToolOutputFile`.
+`llvm::remarks::detail::Remark` and writes YAML/bitstream via
+`llvm::remarks::detail::RemarkStreamer` to a `ToolOutputFile`.
**Ownership**: `MLIRContext` → `RemarkEngine` → `MLIRRemarkStreamerBase`.
+## Categories
+
+There are four built-in categories; you can extend these if needed.
+
+### Passed
+
+A diagnostic indicating the optimization/transformation succeeded.
+
+```c++
+[Passed] Vectorizer:myPass1 {Remark=vectorized loop, tripCount=128}
+```
+
+### Missed
+
+A diagnostic indicating the optimization/transformation did not apply, ideally
+with actionable feedback.
+
+```c++
+[Missed] Unroll: {Reason=tripCount=4 < threshold=256, Suggestion=increase unroll to 128}
+```
+
+### Failure
+
+A diagnostic indicating the optimization/transformation attempted but failed (or
+could not run).
+
+```c++
+[Failure] Unroll: {Reason=failed due to unsupported pattern}
+```
+
+### Analysis
+
+A diagnostic reporting analysis results.
+
+```c++
+[Analysis] Register: {Remark=Kernel uses 168 registers}
+[Analysis] Register: {Remark=Kernel uses 10kB local memory}
+```
+
## Enable Remarks via mlir::emitRemarks (No Streamer)
Enable once per `MLIRContext` (e.g., where you build your pass pipeline or in
@@ -55,7 +94,8 @@ context.enableOptimizationRemarks(/*streamer=*/nullptr,
If you want to persist remarks to a file in YAML or bitstream format, use
`mlir::remark::LLVMRemarkStreamer` (helper shown below):
-You can read more information about [LLVM's Remark from here](https://llvm.org/docs/Remarks.html).
+You can read more information about
+[LLVM's Remark from here](https://llvm.org/docs/Remarks.html).
```c++
#include "mlir/Remark/RemarkStreamer.h"
@@ -84,25 +124,27 @@ LogicalResult MyPass::runOnOperation() {
Location loc = op->getLoc();
// PASS: something succeeded
- reportRemarkPassed(loc, /*category=*/"vectorizer", /*passName=*/"MyPass")
- << "vectorized loop."
- << Remark::RemarkKeyValue("tripCount", 128);
+ remark::passed(loc, categoryVectorizer, myPassname1)
+ << "vectorized loop" << remark::metric("tripCount", 128);
// ANALYSIS: neutral insight
- reportOptimizationAnalysis(loc, "RegisterCount", "")
- << "Kernel uses 168 registers"
+ remark::analysis(loc, categoryRegister, "") << "Kernel uses 168 registers";
// MISSED: explain why + suggest a fix
- reportOptimizationMiss(loc, "unroll", "MyPass",
- /*suggestion=*/[&](){ return "increase unroll factor to >=4"; })
- << "not profitable at this size";
+ int target = 128;
+ int tripBad = 4;
+ int threshold = 256;
+ remark::missed(loc, categoryUnroll)
+ << remark::reason("tripCount={0} < threshold={1}", tripBad, threshold);
+
+ remark::missed(loc, categoryUnroll)
+ << remark::reason("tripCount={0} < threshold={1}", tripBad, threshold)
+ << remark::suggest("increase unroll to {0}", target);
// FAILURE: action attempted but failed
- if (failed(doThing(op))) {
- reportOptimizationFail(loc, "pipeline", "MyPass")
- << "failed due to unsupported pattern";
- return failure();
- }
+ remark::failed(loc, categoryUnroll)
+ << remark::reason("failed due to unsupported pattern");
+
return success();
}
```
diff --git a/mlir/include/mlir/IR/MLIRContext.h b/mlir/include/mlir/IR/MLIRContext.h
index 87786420de37d..2407e4578a41b 100644
--- a/mlir/include/mlir/IR/MLIRContext.h
+++ b/mlir/include/mlir/IR/MLIRContext.h
@@ -34,10 +34,10 @@ class MLIRContextImpl;
class RegisteredOperationName;
class StorageUniquer;
class IRUnit;
-namespace remark {
+namespace remark::detail {
class MLIRRemarkStreamerBase;
class RemarkEngine;
-} // namespace remark
+} // namespace remark::detail
/// MLIRContext is the top-level object for a collection of MLIR operations. It
/// holds immortal uniqued objects like types, and the tables used to unique
@@ -221,7 +221,7 @@ class MLIRContext {
DiagnosticEngine &getDiagEngine();
/// Returns the remark engine for this context.
- remark::RemarkEngine *getRemarkEngine();
+ remark::detail::RemarkEngine *getRemarkEngine();
/// Returns the storage uniquer used for creating affine constructs.
StorageUniquer &getAffineUniquer();
@@ -265,7 +265,7 @@ class MLIRContext {
/// If `printAsEmitRemarks` is true, the remarks will be printed as
/// mlir::emitRemarks.
LogicalResult enableOptimizationRemarks(
- std::unique_ptr<remark::MLIRRemarkStreamerBase> streamer,
+ std::unique_ptr<remark::detail::MLIRRemarkStreamerBase> streamer,
const RemarkCategories &cats, bool printAsEmitRemarks = false);
//===--------------------------------------------------------------------===//
@@ -305,7 +305,7 @@ class MLIRContext {
private:
/// Set the remark engine for this context.
- void setRemarkEngine(std::unique_ptr<remark::RemarkEngine> engine);
+ void setRemarkEngine(std::unique_ptr<remark::detail::RemarkEngine> engine);
/// Return true if the given dialect is currently loading.
bool isDialectLoading(StringRef dialectNamespace);
diff --git a/mlir/include/mlir/IR/Remarks.h b/mlir/include/mlir/IR/Remarks.h
index 563a57f8d725c..e30506bb36224 100644
--- a/mlir/include/mlir/IR/Remarks.h
+++ b/mlir/include/mlir/IR/Remarks.h
@@ -13,12 +13,16 @@
#ifndef MLIR_IR_REMARKS_H
#define MLIR_IR_REMARKS_H
+#include "llvm/ADT/StringExtras.h"
#include "llvm/IR/DiagnosticInfo.h"
#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"
+#include "mlir/IR/Value.h"
namespace mlir::remark {
@@ -30,7 +34,9 @@ enum class RemarkKind {
OptimizationRemarkFailure, // Optimization failure remark.
OptimizationRemarkAnalysis, // Analysis remark that does not indicate a pass.
};
+} // namespace mlir::remark
+namespace mlir::remark::detail {
//===----------------------------------------------------------------------===//
// Remark Base Class
//===----------------------------------------------------------------------===//
@@ -43,29 +49,36 @@ class Remark {
: remarkKind(remarkKind), functionName(functionName), loc(loc),
passName(passName), remarkName(remarkName) {}
- struct RemarkKeyValue {
+ // Remark argument that is a key-value pair that can be printed as machine
+ // parsable args.
+ struct Arg {
std::string key;
std::string val;
-
- explicit RemarkKeyValue(StringRef str = "") : key("String"), val(str) {}
- RemarkKeyValue(StringRef key, Value value);
- RemarkKeyValue(StringRef key, Type type);
- RemarkKeyValue(StringRef key, StringRef s);
- RemarkKeyValue(StringRef key, const char *s)
- : RemarkKeyValue(key, StringRef(s)) {};
- RemarkKeyValue(StringRef key, int n);
- RemarkKeyValue(StringRef key, float n);
- RemarkKeyValue(StringRef key, long n);
- RemarkKeyValue(StringRef key, long long n);
- RemarkKeyValue(StringRef key, unsigned n);
- RemarkKeyValue(StringRef key, unsigned long n);
- RemarkKeyValue(StringRef key, unsigned long long n);
- RemarkKeyValue(StringRef key, bool b)
- : key(key), val(b ? "true" : "false") {}
+ Arg(llvm::StringRef m) : key("Remark"), val(m) {}
+ Arg(llvm::StringRef k, llvm::StringRef v) : key(k), val(v) {}
+ Arg(llvm::StringRef k, std::string v) : key(k), val(std::move(v)) {}
+ Arg(llvm::StringRef k, const char *v) : Arg(k, llvm::StringRef(v)) {}
+ Arg(llvm::StringRef k, Value v);
+ Arg(llvm::StringRef k, Type t);
+ Arg(llvm::StringRef k, bool b) : key(k), val(b ? "true" : "false") {}
+
+ // One constructor for all arithmetic types except bool.
+ template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T> &&
+ !std::is_same_v<T, bool>>>
+ Arg(llvm::StringRef k, T v) : key(k) {
+ if constexpr (std::is_floating_point_v<T>) {
+ llvm::raw_string_ostream os(val);
+ os << v;
+ } else if constexpr (std::is_signed_v<T>) {
+ val = llvm::itostr(static_cast<long long>(v));
+ } else {
+ val = llvm::utostr(static_cast<unsigned long long>(v));
+ }
+ }
};
- void insert(StringRef s);
- void insert(RemarkKeyValue a);
+ void insert(llvm::StringRef s);
+ void insert(Arg a);
void print(llvm::raw_ostream &os, bool printLocation = false) const;
@@ -82,7 +95,7 @@ class Remark {
StringRef getRemarkName() const { return remarkName; }
std::string getMsg() const;
- ArrayRef<RemarkKeyValue> getArgs() const { return args; }
+ ArrayRef<Arg> getArgs() const { return args; }
llvm::remarks::Type getRemarkType() const;
@@ -104,8 +117,8 @@ class Remark {
/// identify the remark.
StringRef remarkName;
- /// RemarkKeyValues collected via the streaming interface.
- SmallVector<RemarkKeyValue, 4> args;
+ /// Args collected via the streaming interface.
+ SmallVector<Arg, 4> args;
private:
/// Convert the MLIR diagnostic severity to LLVM diagnostic severity.
@@ -149,7 +162,7 @@ inline Remark &&operator<<(Remark &&r, StringRef s) {
r.insert(s);
return std::move(r);
}
-inline Remark &operator<<(Remark &r, const Remark::RemarkKeyValue &kv) {
+inline Remark &operator<<(Remark &r, const Remark::Arg &kv) {
r.insert(kv);
return r;
}
@@ -184,6 +197,12 @@ class RemarkEngine;
// InFlightRemark
//===----------------------------------------------------------------------===//
+/// Lazy text building for zero cost string formatting.
+struct LazyTextBuild {
+ llvm::StringRef key;
+ std::function<std::string()> thunk;
+};
+
/// InFlightRemark is a RAII class that holds a reference to a Remark
/// instance and allows to build the remark using the << operator. The remark
/// is emitted when the InFlightRemark instance is destroyed, which happens
@@ -197,7 +216,15 @@ class InFlightRemark {
InFlightRemark() = default; // empty ctor
- template <typename T>
+ InFlightRemark &operator<<(const LazyTextBuild &l) {
+ if (remark)
+ *remark << Remark::Arg(l.key, l.thunk());
+ return *this;
+ }
+
+ // Generic path, but *not* for Lazy
+ template <typename T, typename = std::enable_if_t<
+ !std::is_same_v<std::decay_t<T>, LazyTextBuild>>>
InFlightRemark &operator<<(T &&arg) {
if (remark)
*remark << std::forward<T>(arg);
@@ -334,18 +361,8 @@ class RemarkEngine {
StringRef category);
};
-using RemarkKV = remark::Remark::RemarkKeyValue;
-
-} // namespace mlir::remark
-
-//===----------------------------------------------------------------------===//
-// Emitters
-//===----------------------------------------------------------------------===//
-namespace mlir {
-using namespace mlir::remark;
-
template <typename Fn, typename... Args>
-inline remark::InFlightRemark withEngine(Fn fn, Location loc, Args &&...args) {
+inline InFlightRemark withEngine(Fn fn, Location loc, Args &&...args) {
MLIRContext *ctx = loc->getContext();
RemarkEngine *enginePtr = ctx->getRemarkEngine();
@@ -356,38 +373,60 @@ inline remark::InFlightRemark withEngine(Fn fn, Location loc, Args &&...args) {
return {};
}
-/// Report an optimization remark that was passed.
-inline InFlightRemark reportRemarkPassed(Location loc, StringRef cat,
- StringRef passName) {
- return withEngine(&RemarkEngine::emitOptimizationRemark, loc, passName, cat);
+} // namespace mlir::remark::detail
+
+namespace mlir::remark {
+
+template <class... Ts>
+inline detail::LazyTextBuild reason(const char *fmt, Ts &&...ts) {
+ return {"Reason", [=] { return llvm::formatv(fmt, ts...).str(); }};
}
-inline RemarkKV suggest(StringRef s) { return {"Suggestion", s}; }
+template <class... Ts>
+inline detail::LazyTextBuild suggest(const char *fmt, Ts &&...ts) {
+ return {"Suggestion", [=] { return llvm::formatv(fmt, ts...).str(); }};
+}
+
+template <class V>
+inline detail::LazyTextBuild metric(StringRef key, V &&v) {
+ using DV = std::decay_t<V>;
+ return {key, [key, vv = DV(std::forward<V>(v))]() mutable {
+ // Reuse Arg's formatting logic and return just the value string.
+ return detail::Remark::Arg(key, std::move(vv)).val;
+ }};
+}
+//===----------------------------------------------------------------------===//
+// Emitters
+//===----------------------------------------------------------------------===//
+
+/// Report an optimization remark that was passed.
+inline detail::InFlightRemark passed(Location loc, StringRef cat,
+ StringRef passName = {}) {
+ return withEngine(&detail::RemarkEngine::emitOptimizationRemark, loc,
+ passName, cat);
+}
/// Report an optimization remark that was missed.
-inline InFlightRemark
-reportOptimizationMiss(Location loc, StringRef cat, StringRef passName,
- llvm::function_ref<std::string()> makeSuggestion) {
- auto r =
- withEngine(&RemarkEngine::emitOptimizationRemarkMiss, loc, passName, cat);
- if (r)
- r << suggest(makeSuggestion());
- return r;
+inline detail::InFlightRemark missed(Location loc, llvm::StringRef cat,
+ llvm::StringRef passName = {}) {
+ return withEngine(&detail::RemarkEngine::emitOptimizationRemarkMiss, loc,
+ passName, cat);
}
-/// Report an optimization failure remark.
-inline InFlightRemark reportOptimizationFail(Location loc, StringRef cat,
- StringRef passName) {
- return withEngine(&RemarkEngine::emitOptimizationRemarkFailure, loc, passName,
- cat);
+
+/// Report an optimization remark that failed.
+inline detail::InFlightRemark failed(Location loc, llvm::StringRef cat,
+ llvm::StringRef passName = {}) {
+ return withEngine(&detail::RemarkEngine::emitOptimizationRemarkFailure, loc,
+ passName, cat);
}
/// Report an optimization analysis remark.
-inline InFlightRemark reportOptimizationAnalysis(Location loc, StringRef cat,
- StringRef passName) {
- return withEngine(&RemarkEngine::emitOptimizationRemarkAnalysis, loc,
+inline detail::InFlightRemark analysis(Location loc, StringRef cat,
+ StringRef passName = {}) {
+ return withEngine(&detail::RemarkEngine::emitOptimizationRemarkAnalysis, loc,
passName, cat);
}
-} // namespace mlir
+} // namespace mlir::remark
#endif // MLIR_IR_REMARKS_H
diff --git a/mlir/include/mlir/Remark/RemarkStreamer.h b/mlir/include/mlir/Remark/RemarkStreamer.h
index 3c055442bffd6..532098170ab97 100644
--- a/mlir/include/mlir/Remark/RemarkStreamer.h
+++ b/mlir/include/mlir/Remark/RemarkStreamer.h
@@ -1,10 +1,24 @@
+//===- RemarkStreamer.h - MLIR Optimization Remark ----------------------*-
+// C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines LLVMRemarkStreamer plugging class that uses LLVM's
+// streamer.
+//
+//===----------------------------------------------------------------------===//
+
#include "mlir/IR/Remarks.h"
#include "llvm/Remarks/RemarkStreamer.h"
#include "llvm/Support/ToolOutputFile.h"
using namespace llvm;
-namespace mlir::remark {
+namespace mlir::remark::detail {
/// Concrete streamer that writes LLVM optimization remarks to a file
/// (YAML or Bitstream). Lives outside core.
@@ -23,7 +37,9 @@ class LLVMRemarkStreamer final : public MLIRRemarkStreamerBase {
std::unique_ptr<class llvm::remarks::RemarkStreamer> remarkStreamer;
std::unique_ptr<class llvm::ToolOutputFile> file;
};
+} // namespace mlir::remark::detail
+namespace mlir::remark {
/// Enable optimization remarks to a file with the given path and format.
/// The remark categories are used to filter the remarks that are emitted.
/// If the printAsEmitRemarks flag is set, remarks will also be printed using
diff --git a/mlir/lib/IR/MLIRContext.cpp b/mlir/lib/IR/MLIRContext.cpp
index 55cbac4b2f570..5d05e4edc0296 100644
--- a/mlir/lib/IR/MLIRContext.cpp
+++ b/mlir/lib/IR/MLIRContext.cpp
@@ -138,7 +138,7 @@ class MLIRContextImpl {
//===--------------------------------------------------------------------===//
// Remark
//===--------------------------------------------------------------------===//
- std::unique_ptr<remark::RemarkEngine> remarkEngine;
+ std::unique_ptr<remark::detail::RemarkEngine> remarkEngine;
//===--------------------------------------------------------------------===//
// Options
@@ -400,19 +400,19 @@ DiagnosticEngine &MLIRContext::getDiagEngine() { return getImpl().diagEngine; }
/// Returns the remark engine for this context.
void MLIRContext::setRemarkEngine(
- std::unique_ptr<remark::RemarkEngine> engine) {
+ std::unique_ptr<remark::detail::RemarkEngine> engine) {
getImpl().remarkEngine = std::move(engine);
}
-remark::RemarkEngine *MLIRContext::getRemarkEngine() {
+remark::detail::RemarkEngine *MLIRContext::getRemarkEngine() {
return getImpl().remarkEngine.get();
}
LogicalResult MLIRContext::enableOptimizationRemarks(
- std::unique_ptr<remark::MLIRRemarkStreamerBase> streamer,
+ std::unique_ptr<remark::detail::MLIRRemarkStreamerBase> streamer,
const RemarkCategories &cats, bool printAsEmitRemarks) {
auto engine =
- std::make_unique<remark::RemarkEngine>(printAsEmitRemarks, cats);
+ std::make_unique<remark::detail::RemarkEngine>(printAsEmitRemarks, cats);
std::string errMsg;
if (failed(engine->initialize(std::move(streamer), &errMsg))) {
diff --git a/mlir/lib/IR/Remarks.cpp b/mlir/lib/IR/Remarks.cpp
index 3dac417037308..3c204a14100de 100644
--- a/mlir/lib/IR/Remarks.cpp
+++ b/mlir/lib/IR/Remarks.cpp
@@ -15,56 +15,27 @@
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
-using namespace mlir::remark;
+using namespace mlir::remark::detail;
//------------------------------------------------------------------------------
// Remark
//------------------------------------------------------------------------------
-Remark::RemarkKeyValue::RemarkKeyValue(StringRef key, Value value)
- : key(std::string(key)) {
-
- llvm::raw_string_ostream rss(val);
- rss << value;
+Remark::Arg::Arg(llvm::StringRef k, Value v) : key(k) {
+ llvm::raw_string_ostream os(val);
+ os << v;
}
-Remark::RemarkKeyValue::RemarkKeyValue(StringRef key, Type type)
- : key(std::string(key)) {
+Remark::Arg::Arg(llvm::StringRef k, Type t) : key(k) {
llvm::raw_string_ostream os(val);
- os << type;
+ os << t;
}
-Remark::RemarkKeyValue::RemarkKeyValue(StringRef key, StringRef s)
- : key(std::string(key)), val(s.str()) {}
-
-Remark::RemarkKeyValue::RemarkKeyValue(StringRef key, int n)
- : key(std::string(key)), val(llvm::itostr(n)) {}
-
-Remark::RemarkKeyValue::RemarkKeyValue(StringRef key, float n)
- : key(std::string(key)), val(std::to_string(n)) {}
-
-Remark::RemarkKeyValue::RemarkKeyValue(StringRef key, long n)
- : key(std::string(key)), val(llvm::itostr(n)) {}
-
-Remark::RemarkKeyValue::RemarkKeyValue(StringRef key, long long n)
- : key(std::string(key)), val(llvm::itostr(n)) {}
-
-Remark::RemarkKeyValue::RemarkKeyValue(StringRef key, unsigned n)
- : key(std::string(key)), val(llvm::utostr(n)) {}
-
-Remark::RemarkKeyValue::RemarkKeyValue(StringRef key, unsigned long n)
- : key(std::string(key)), val(llvm::utostr(n)) {}
-
-Remark::RemarkKeyValue::RemarkKeyValue(StringRef key, unsigned long long n)
- : key(std::string(key)), val(llvm::utostr(n)) {}
-
-void Remark::insert(StringRef s) { args.emplace_back(s); }
-
-void Remark::insert(RemarkKeyValue a) { args.push_back(std::move(a)); }
+void Remark::insert(llvm::StringRef s) { args.emplace_back(s); }
+void Remark::insert(Arg a) { args.push_back(std::move(a)); }
// Simple helper to print key=val list.
-static void printArgs(llvm::raw_ostream &os,
- llvm::ArrayRef<Remark::RemarkKeyValue> args) {
+static void printArgs(llvm::raw_ostream &os, llvm::ArrayRef<Remark::Arg> args) {
if (args.empty())
return;
os << " {";
@@ -149,7 +120,7 @@ llvm::remarks::Remark Remark::generateRemark() const {
r.RemarkName = getRemarkName();
r.FunctionName = getFunction();
r.Loc = locLambda();
- for (const Remark::RemarkKeyValue &arg : getArgs()) {
+ for (const Remark::Arg &arg : getArgs()) {
r.Args.emplace_back();
r.Args.back().Key = arg.key;
r.Args.back().Val = arg.val;
diff --git a/mlir/lib/Remark/RemarkStreamer.cpp b/mlir/lib/Remark/RemarkStreamer.cpp
index b6b4f8bc660c3..3ca99746c1f9f 100644
--- a/mlir/lib/Remark/RemarkStreamer.cpp
+++ b/mlir/lib/Remark/RemarkStreamer.cpp
@@ -8,7 +8,7 @@
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/ToolOutputFile.h"
-namespace mlir::remark {
+namespace mlir::remark::detail {
FailureOr<std::unique_ptr<MLIRRemarkStreamerBase>>
LLVMRemarkStreamer::createToFile(llvm::StringRef path,
@@ -33,7 +33,7 @@ LLVMRemarkStreamer::createToFile(llvm::StringRef path,
auto impl = std::unique_ptr<LLVMRemarkStreamer>(new LLVMRemarkStreamer());
impl->remarkStreamer = std::move(rs);
impl->file = std::move(f);
- return std::unique_ptr<mlir::remark::MLIRRemarkStreamerBase>(std::move(impl));
+ return std::unique_ptr<MLIRRemarkStreamerBase>(std::move(impl));
}
void LLVMRemarkStreamer::streamOptimizationRemark(const Remark &remark) {
@@ -50,13 +50,15 @@ LLVMRemarkStreamer::~LLVMRemarkStreamer() {
if (file && remarkStreamer)
file->keep();
}
+} // namespace mlir::remark::detail
+namespace mlir::remark {
LogicalResult enableOptimizationRemarksToFile(
MLIRContext &ctx, StringRef path, llvm::remarks::Format fmt,
const MLIRContext::RemarkCategories &cat, bool printAsEmitRemarks) {
- FailureOr<std::unique_ptr<MLIRRemarkStreamerBase>> sOr =
- LLVMRemarkStreamer::createToFile(path, fmt);
+ FailureOr<std::unique_ptr<detail::MLIRRemarkStreamerBase>> sOr =
+ detail::LLVMRemarkStreamer::createToFile(path, fmt);
if (failed(sOr))
return failure();
return ctx.enableOptimizationRemarks(std::move(*sOr), cat,
diff --git a/mlir/unittests/IR/RemarkTest.cpp b/mlir/unittests/IR/RemarkTest.cpp
index ba4981260b137..9106cfeaba3cd 100644
--- a/mlir/unittests/IR/RemarkTest.cpp
+++ b/mlir/unittests/IR/RemarkTest.cpp
@@ -22,20 +22,14 @@
using namespace llvm;
using namespace mlir;
-using namespace mlir::detail;
namespace {
TEST(Remark, TestOutputOptimizationRemark) {
- const auto *pass1Msg = "My message";
- const auto *pass2Msg = "My another message";
- const auto *pass3Msg = "Do not show this message";
-
- std::string categoryLoopunroll("LoopUnroll");
- std::string categoryInline("Inliner");
+ std::string categoryVectorizer("Vectorizer");
+ std::string categoryRegister("Register");
+ std::string categoryUnroll("Unroll");
std::string myPassname1("myPass1");
- std::string myPassname2("myPass2");
- std::string funcName("myFunc");
SmallString<64> tmpPathStorage;
sys::fs::createUniquePath("remarks-%%%%%%.yaml", tmpPathStorage,
/*MakeAbsolute=*/true);
@@ -51,45 +45,36 @@ TEST(Remark, TestOutputOptimizationRemark) {
context.printStackTraceOnDiagnostic(true);
// Setup the remark engine
- mlir::MLIRContext::RemarkCategories cats{/*passed=*/categoryLoopunroll,
- /*missed=*/std::nullopt,
- /*analysis=*/std::nullopt,
- /*failed=*/categoryLoopunroll};
+ mlir::MLIRContext::RemarkCategories cats{/*passed=*/categoryVectorizer,
+ /*missed=*/categoryUnroll,
+ /*analysis=*/categoryRegister,
+ /*failed=*/categoryUnroll};
LogicalResult isEnabled = mlir::remark::enableOptimizationRemarksToFile(
context, yamlFile, llvm::remarks::Format::YAML, cats);
ASSERT_TRUE(succeeded(isEnabled)) << "Failed to enable remark engine";
- // Remark 1: pass, category LoopUnroll
- reportRemarkPassed(loc, categoryLoopunroll, myPassname1) << pass1Msg;
- // Remark 2: failure, category LoopUnroll
- reportOptimizationFail(loc, categoryLoopunroll, myPassname2) << pass2Msg;
- // Remark 3: pass, category Inline (should not be printed)
- reportRemarkPassed(loc, categoryInline, myPassname1) << pass3Msg;
+ // PASS: something succeeded
+ remark::passed(loc, categoryVectorizer, myPassname1)
+ << "vectorized loop" << remark::metric("tripCount", 128);
+
+ // ANALYSIS: neutral insight
+ remark::analysis(loc, categoryRegister, "") << "Kernel uses 168 registers";
+
+ // MISSED: explain why + suggest a fix
+ remark::missed(loc, categoryUnroll, "MyPass")
+ << remark::reason("not profitable at this size"),
+ remark::suggest("increase unroll factor to >=4");
+
+ // FAILURE: action attempted but failed
+ remark::failed(loc, categoryUnroll, "MyPass")
+ << remark::reason("failed due to unsupported pattern");
}
// Read the file
auto bufferOrErr = MemoryBuffer::getFile(yamlFile);
ASSERT_TRUE(static_cast<bool>(bufferOrErr)) << "Failed to open remarks file";
std::string content = bufferOrErr.get()->getBuffer().str();
-
- // Remark 1: pass, should be printed
- EXPECT_NE(content.find("--- !Passed"), std::string::npos);
- EXPECT_NE(content.find("Pass: " + categoryLoopunroll),
- std::string::npos);
- EXPECT_NE(content.find("Name: " + myPassname1), std::string::npos);
- EXPECT_NE(content.find("String: " + std::string(pass1Msg)),
- std::string::npos);
-
- // Remark 2: failure, should be printed
- EXPECT_NE(content.find("--- !Failure"), std::string::npos);
- EXPECT_NE(content.find("Name: " + myPassname2), std::string::npos);
- EXPECT_NE(content.find("String: " + std::string(pass2Msg)),
- std::string::npos);
-
- // Remark 3: pass, category Inline (should not be printed)
- EXPECT_EQ(content.find("String: " + std::string(pass3Msg)),
- std::string::npos);
}
TEST(Remark, TestNoOutputOptimizationRemark) {
@@ -112,7 +97,8 @@ TEST(Remark, TestNoOutputOptimizationRemark) {
{
MLIRContext context;
Location loc = UnknownLoc::get(&context);
- reportOptimizationFail(loc, categoryFailName, myPassname1) << pass1Msg;
+ remark::failed(loc, categoryFailName, myPassname1)
+ << remark::reason(pass1Msg);
}
// No setup, so no output file should be created
// check!
@@ -122,14 +108,12 @@ TEST(Remark, TestNoOutputOptimizationRemark) {
}
TEST(Remark, TestOutputOptimizationRemarkDiagnostic) {
- const auto *pass1Msg = "My message";
-
- std::string categoryLoopunroll("LoopUnroll");
+ std::string categoryVectorizer("Vectorizer");
+ std::string categoryRegister("Register");
+ std::string categoryUnroll("Unroll");
std::string myPassname1("myPass1");
- std::string funcName("myFunc");
- std::string seenMsg = "";
- std::string expectedMsg = "[Passed] LoopUnroll:myPass1 {String=My message}";
+ llvm::SmallVector<std::string> seenMsg;
{
MLIRContext context;
Location loc = UnknownLoc::get(&context);
@@ -139,32 +123,59 @@ TEST(Remark, TestOutputOptimizationRemarkDiagnostic) {
// Register a handler that captures the diagnostic.
ScopedDiagnosticHandler handler(&context, [&](Diagnostic &diag) {
- seenMsg = diag.str();
+ seenMsg.push_back(diag.str());
return success();
});
// Setup the remark engine
- mlir::MLIRContext::RemarkCategories cats{/*passed=*/categoryLoopunroll,
- /*missed=*/std::nullopt,
- /*analysis=*/std::nullopt,
- /*failed=*/categoryLoopunroll};
+ mlir::MLIRContext::RemarkCategories cats{/*passed=*/categoryVectorizer,
+ /*missed=*/categoryUnroll,
+ /*analysis=*/categoryRegister,
+ /*failed=*/categoryUnroll};
LogicalResult isEnabled =
context.enableOptimizationRemarks(nullptr, cats, true);
+
ASSERT_TRUE(succeeded(isEnabled)) << "Failed to enable remark engine";
- // Remark 1: pass, category LoopUnroll
- reportRemarkPassed(loc, categoryLoopunroll, myPassname1) << pass1Msg;
+ // PASS: something succeeded
+ remark::passed(loc, categoryVectorizer, myPassname1)
+ << "vectorized loop" << remark::metric("tripCount", 128);
+
+ // ANALYSIS: neutral insight
+ remark::analysis(loc, categoryRegister, "") << "Kernel uses 168 registers";
+
+ // MISSED: explain why + suggest a fix
+ int target = 128;
+ int tripBad = 4;
+ int threshold = 256;
+
+ remark::missed(loc, categoryUnroll)
+ << remark::reason("tripCount={0} < threshold={1}", tripBad, threshold);
+
+ remark::missed(loc, categoryUnroll)
+ << remark::reason("tripCount={0} < threshold={1}", tripBad, threshold)
+ << remark::suggest("increase unroll to {0}", target);
+
+ // FAILURE: action attempted but failed
+ remark::failed(loc, categoryUnroll)
+ << remark::reason("failed due to unsupported pattern");
}
- EXPECT_EQ(seenMsg, expectedMsg);
+ // clang-format off
+ EXPECT_EQ(seenMsg[0], "[Passed] Vectorizer:myPass1 {Remark=vectorized loop, tripCount=128}");
+ EXPECT_EQ(seenMsg[1], "[Analysis] Register: {Remark=Kernel uses 168 registers}");
+ EXPECT_EQ(seenMsg[2], "[Missed] Unroll: {Reason=tripCount=4 < threshold=256}");
+ EXPECT_EQ(seenMsg[3], "[Missed] Unroll: {Reason=tripCount=4 < threshold=256, Suggestion=increase unroll to 128}");
+ EXPECT_EQ(seenMsg[4], "[Failure] Unroll: {Reason=failed due to unsupported pattern}");
+ // clang-format on
}
/// Custom remark streamer that prints remarks to stderr.
-class MyCustomStreamer : public MLIRRemarkStreamerBase {
+class MyCustomStreamer : public remark::detail::MLIRRemarkStreamerBase {
public:
MyCustomStreamer() = default;
- void streamOptimizationRemark(const Remark &remark) override {
+ void streamOptimizationRemark(const remark::detail::Remark &remark) override {
llvm::errs() << "Custom remark: ";
remark.print(llvm::errs(), true);
llvm::errs() << "\n";
@@ -200,11 +211,12 @@ TEST(Remark, TestCustomOptimizationRemarkDiagnostic) {
ASSERT_TRUE(succeeded(isEnabled)) << "Failed to enable remark engine";
// Remark 1: pass, category LoopUnroll
- reportRemarkPassed(loc, categoryLoopunroll, myPassname1) << pass1Msg;
+ remark::passed(loc, categoryLoopunroll, myPassname1) << pass1Msg;
// Remark 2: failure, category LoopUnroll
- reportOptimizationFail(loc, categoryLoopunroll, myPassname2) << pass2Msg;
+ remark::failed(loc, categoryLoopunroll, myPassname2)
+ << remark::reason(pass2Msg);
// Remark 3: pass, category Inline (should not be printed)
- reportRemarkPassed(loc, categoryInline, myPassname1) << pass3Msg;
+ remark::passed(loc, categoryInline, myPassname1) << pass3Msg;
}
llvm::errs().flush();
>From 2bd50fd5a428c26d9daf86bdad54728ed45187a1 Mon Sep 17 00:00:00 2001
From: Guray Ozen <gozen at nvidia.com>
Date: Tue, 12 Aug 2025 15:22:08 +0000
Subject: [PATCH 11/22] move RemarkCategories-> mlir::remark
---
mlir/include/mlir/IR/MLIRContext.h | 8 ++++----
mlir/include/mlir/IR/Remarks.h | 6 ++++--
mlir/include/mlir/Remark/RemarkStreamer.h | 7 ++++---
mlir/lib/IR/MLIRContext.cpp | 2 +-
mlir/lib/IR/Remarks.cpp | 2 +-
mlir/lib/Remark/RemarkStreamer.cpp | 7 ++++---
mlir/unittests/IR/RemarkTest.cpp | 24 +++++++++++------------
7 files changed, 30 insertions(+), 26 deletions(-)
diff --git a/mlir/include/mlir/IR/MLIRContext.h b/mlir/include/mlir/IR/MLIRContext.h
index 2407e4578a41b..25a07962dd863 100644
--- a/mlir/include/mlir/IR/MLIRContext.h
+++ b/mlir/include/mlir/IR/MLIRContext.h
@@ -34,6 +34,9 @@ class MLIRContextImpl;
class RegisteredOperationName;
class StorageUniquer;
class IRUnit;
+namespace remark {
+struct RemarkCategories;
+}
namespace remark::detail {
class MLIRRemarkStreamerBase;
class RemarkEngine;
@@ -64,9 +67,6 @@ class RemarkEngine;
class MLIRContext {
public:
enum class Threading { DISABLED, ENABLED };
- struct RemarkCategories {
- std::optional<std::string> passed, missed, analysis, failed;
- };
/// Create a new Context.
explicit MLIRContext(Threading multithreading = Threading::ENABLED);
@@ -266,7 +266,7 @@ class MLIRContext {
/// mlir::emitRemarks.
LogicalResult enableOptimizationRemarks(
std::unique_ptr<remark::detail::MLIRRemarkStreamerBase> streamer,
- const RemarkCategories &cats, bool printAsEmitRemarks = false);
+ const remark::RemarkCategories &cats, bool printAsEmitRemarks = false);
//===--------------------------------------------------------------------===//
// Action API
diff --git a/mlir/include/mlir/IR/Remarks.h b/mlir/include/mlir/IR/Remarks.h
index e30506bb36224..7ba7a190324e4 100644
--- a/mlir/include/mlir/IR/Remarks.h
+++ b/mlir/include/mlir/IR/Remarks.h
@@ -25,6 +25,9 @@
#include "mlir/IR/Value.h"
namespace mlir::remark {
+struct RemarkCategories {
+ std::optional<std::string> passed, missed, analysis, failed;
+};
/// Defines different remark kinds that can be used to categorize remarks.
enum class RemarkKind {
@@ -328,8 +331,7 @@ class RemarkEngine {
/// Constructs Remark engine with optional category names. If a category
/// name is not provided, it is not enabled. The category names are used to
/// filter the remarks that are emitted.
- RemarkEngine(bool printAsEmitRemarks,
- const MLIRContext::RemarkCategories &cats);
+ RemarkEngine(bool printAsEmitRemarks, const RemarkCategories &cats);
/// Destructor that will close the output file and reset the
/// main remark streamer.
diff --git a/mlir/include/mlir/Remark/RemarkStreamer.h b/mlir/include/mlir/Remark/RemarkStreamer.h
index 532098170ab97..dd3b269c27faf 100644
--- a/mlir/include/mlir/Remark/RemarkStreamer.h
+++ b/mlir/include/mlir/Remark/RemarkStreamer.h
@@ -44,8 +44,9 @@ namespace mlir::remark {
/// The remark categories are used to filter the remarks that are emitted.
/// If the printAsEmitRemarks flag is set, remarks will also be printed using
/// mlir::emitRemarks.
-LogicalResult enableOptimizationRemarksToFile(
- MLIRContext &ctx, StringRef path, llvm::remarks::Format fmt,
- const MLIRContext::RemarkCategories &cat, bool printAsEmitRemarks = false);
+LogicalResult enableOptimizationRemarksToFile(MLIRContext &ctx, StringRef path,
+ llvm::remarks::Format fmt,
+ const RemarkCategories &cat,
+ bool printAsEmitRemarks = false);
} // namespace mlir::remark
diff --git a/mlir/lib/IR/MLIRContext.cpp b/mlir/lib/IR/MLIRContext.cpp
index 5d05e4edc0296..0f68af77a598b 100644
--- a/mlir/lib/IR/MLIRContext.cpp
+++ b/mlir/lib/IR/MLIRContext.cpp
@@ -410,7 +410,7 @@ remark::detail::RemarkEngine *MLIRContext::getRemarkEngine() {
LogicalResult MLIRContext::enableOptimizationRemarks(
std::unique_ptr<remark::detail::MLIRRemarkStreamerBase> streamer,
- const RemarkCategories &cats, bool printAsEmitRemarks) {
+ const remark::RemarkCategories &cats, bool printAsEmitRemarks) {
auto engine =
std::make_unique<remark::detail::RemarkEngine>(printAsEmitRemarks, cats);
diff --git a/mlir/lib/IR/Remarks.cpp b/mlir/lib/IR/Remarks.cpp
index 3c204a14100de..752d3d29b263b 100644
--- a/mlir/lib/IR/Remarks.cpp
+++ b/mlir/lib/IR/Remarks.cpp
@@ -232,7 +232,7 @@ RemarkEngine::initialize(std::unique_ptr<MLIRRemarkStreamerBase> streamer,
}
RemarkEngine::RemarkEngine(bool printAsEmitRemarks,
- const MLIRContext::RemarkCategories &cats)
+ const RemarkCategories &cats)
: printAsEmitRemarks(printAsEmitRemarks) {
if (cats.passed)
passFilter = llvm::Regex(cats.passed.value());
diff --git a/mlir/lib/Remark/RemarkStreamer.cpp b/mlir/lib/Remark/RemarkStreamer.cpp
index 3ca99746c1f9f..e862543b682fe 100644
--- a/mlir/lib/Remark/RemarkStreamer.cpp
+++ b/mlir/lib/Remark/RemarkStreamer.cpp
@@ -53,9 +53,10 @@ LLVMRemarkStreamer::~LLVMRemarkStreamer() {
} // namespace mlir::remark::detail
namespace mlir::remark {
-LogicalResult enableOptimizationRemarksToFile(
- MLIRContext &ctx, StringRef path, llvm::remarks::Format fmt,
- const MLIRContext::RemarkCategories &cat, bool printAsEmitRemarks) {
+LogicalResult enableOptimizationRemarksToFile(MLIRContext &ctx, StringRef path,
+ llvm::remarks::Format fmt,
+ const RemarkCategories &cat,
+ bool printAsEmitRemarks) {
FailureOr<std::unique_ptr<detail::MLIRRemarkStreamerBase>> sOr =
detail::LLVMRemarkStreamer::createToFile(path, fmt);
diff --git a/mlir/unittests/IR/RemarkTest.cpp b/mlir/unittests/IR/RemarkTest.cpp
index 9106cfeaba3cd..1f9158055a13b 100644
--- a/mlir/unittests/IR/RemarkTest.cpp
+++ b/mlir/unittests/IR/RemarkTest.cpp
@@ -45,10 +45,10 @@ TEST(Remark, TestOutputOptimizationRemark) {
context.printStackTraceOnDiagnostic(true);
// Setup the remark engine
- mlir::MLIRContext::RemarkCategories cats{/*passed=*/categoryVectorizer,
- /*missed=*/categoryUnroll,
- /*analysis=*/categoryRegister,
- /*failed=*/categoryUnroll};
+ mlir::remark::RemarkCategories cats{/*passed=*/categoryVectorizer,
+ /*missed=*/categoryUnroll,
+ /*analysis=*/categoryRegister,
+ /*failed=*/categoryUnroll};
LogicalResult isEnabled = mlir::remark::enableOptimizationRemarksToFile(
context, yamlFile, llvm::remarks::Format::YAML, cats);
@@ -128,10 +128,10 @@ TEST(Remark, TestOutputOptimizationRemarkDiagnostic) {
});
// Setup the remark engine
- mlir::MLIRContext::RemarkCategories cats{/*passed=*/categoryVectorizer,
- /*missed=*/categoryUnroll,
- /*analysis=*/categoryRegister,
- /*failed=*/categoryUnroll};
+ mlir::remark::RemarkCategories cats{/*passed=*/categoryVectorizer,
+ /*missed=*/categoryUnroll,
+ /*analysis=*/categoryRegister,
+ /*failed=*/categoryUnroll};
LogicalResult isEnabled =
context.enableOptimizationRemarks(nullptr, cats, true);
@@ -201,10 +201,10 @@ TEST(Remark, TestCustomOptimizationRemarkDiagnostic) {
Location loc = UnknownLoc::get(&context);
// Setup the remark engine
- mlir::MLIRContext::RemarkCategories cats{/*passed=*/categoryLoopunroll,
- /*missed=*/std::nullopt,
- /*analysis=*/std::nullopt,
- /*failed=*/categoryLoopunroll};
+ mlir::remark::RemarkCategories cats{/*passed=*/categoryLoopunroll,
+ /*missed=*/std::nullopt,
+ /*analysis=*/std::nullopt,
+ /*failed=*/categoryLoopunroll};
LogicalResult isEnabled = context.enableOptimizationRemarks(
std::make_unique<MyCustomStreamer>(), cats, true);
>From a825d7f5b8cfb934528f416713fb68bebeb73a07 Mon Sep 17 00:00:00 2001
From: Guray Ozen <gozen at nvidia.com>
Date: Tue, 12 Aug 2025 15:23:39 +0000
Subject: [PATCH 12/22] fix copilot
---
mlir/docs/Remarks.md | 4 ++--
mlir/include/mlir/Remark/RemarkStreamer.h | 1 -
mlir/lib/IR/Remarks.cpp | 2 +-
mlir/lib/Remark/CMakeLists.txt | 2 +-
4 files changed, 4 insertions(+), 5 deletions(-)
diff --git a/mlir/docs/Remarks.md b/mlir/docs/Remarks.md
index 8bf28a1f19b22..22731d25eb507 100644
--- a/mlir/docs/Remarks.md
+++ b/mlir/docs/Remarks.md
@@ -79,7 +79,7 @@ context’s `DiagnosticEngine` under the provided category labels—handy for
interactive tools and tests.
```c++
-mlir::MLIRContext::RemarkCategories cats{/*passed=*/categoryLoopunroll,
+mlir::remark::RemarkCategories cats{/*passed=*/categoryLoopunroll,
/*missed=*/std::nullopt,
/*analysis=*/std::nullopt,
/*failed=*/categoryLoopunroll};
@@ -100,7 +100,7 @@ You can read more information about
```c++
#include "mlir/Remark/RemarkStreamer.h"
-mlir::MLIRContext::RemarkCategories cats{/*passed=*/categoryLoopunroll,
+mlir::remark::RemarkCategories cats{/*passed=*/categoryLoopunroll,
/*missed=*/std::nullopt,
/*analysis=*/std::nullopt,
/*failed=*/categoryLoopunroll};
diff --git a/mlir/include/mlir/Remark/RemarkStreamer.h b/mlir/include/mlir/Remark/RemarkStreamer.h
index dd3b269c27faf..148629223b530 100644
--- a/mlir/include/mlir/Remark/RemarkStreamer.h
+++ b/mlir/include/mlir/Remark/RemarkStreamer.h
@@ -17,7 +17,6 @@
#include "llvm/Remarks/RemarkStreamer.h"
#include "llvm/Support/ToolOutputFile.h"
-using namespace llvm;
namespace mlir::remark::detail {
/// Concrete streamer that writes LLVM optimization remarks to a file
diff --git a/mlir/lib/IR/Remarks.cpp b/mlir/lib/IR/Remarks.cpp
index 752d3d29b263b..abc042d0e4cb3 100644
--- a/mlir/lib/IR/Remarks.cpp
+++ b/mlir/lib/IR/Remarks.cpp
@@ -205,7 +205,7 @@ RemarkEngine::emitOptimizationRemarkAnalysis(Location loc, StringRef passName,
}
//===----------------------------------------------------------------------===//
-// Remarkengine
+// RemarkEngine
//===----------------------------------------------------------------------===//
void RemarkEngine::report(const Remark &&remark) {
diff --git a/mlir/lib/Remark/CMakeLists.txt b/mlir/lib/Remark/CMakeLists.txt
index 4888d71a7ad66..920a95db4f07b 100644
--- a/mlir/lib/Remark/CMakeLists.txt
+++ b/mlir/lib/Remark/CMakeLists.txt
@@ -10,5 +10,5 @@ add_mlir_library(MLIRRemarkStreamer
LINK_COMPONENTS
Remarks
Core
- BitstreamReader
+ BitstreamReader
)
>From 2fbb84a7551617b9eafe5d17ca6a5eac76517650 Mon Sep 17 00:00:00 2001
From: Guray Ozen <gozen at nvidia.com>
Date: Tue, 12 Aug 2025 15:26:45 +0000
Subject: [PATCH 13/22] fx
---
mlir/unittests/IR/RemarkTest.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/mlir/unittests/IR/RemarkTest.cpp b/mlir/unittests/IR/RemarkTest.cpp
index 1f9158055a13b..7b2bef9f62724 100644
--- a/mlir/unittests/IR/RemarkTest.cpp
+++ b/mlir/unittests/IR/RemarkTest.cpp
@@ -63,8 +63,8 @@ TEST(Remark, TestOutputOptimizationRemark) {
// MISSED: explain why + suggest a fix
remark::missed(loc, categoryUnroll, "MyPass")
- << remark::reason("not profitable at this size"),
- remark::suggest("increase unroll factor to >=4");
+ << remark::reason("not profitable at this size")
+ << remark::suggest("increase unroll factor to >=4");
// FAILURE: action attempted but failed
remark::failed(loc, categoryUnroll, "MyPass")
>From 0c4b3866c19663ba5ebb4138af4bf0892f4ed5b6 Mon Sep 17 00:00:00 2001
From: Guray Ozen <gozen at nvidia.com>
Date: Tue, 12 Aug 2025 16:00:52 +0000
Subject: [PATCH 14/22] fix comment
---
mlir/include/mlir/IR/MLIRContext.h | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/mlir/include/mlir/IR/MLIRContext.h b/mlir/include/mlir/IR/MLIRContext.h
index 25a07962dd863..6c3aac418fa79 100644
--- a/mlir/include/mlir/IR/MLIRContext.h
+++ b/mlir/include/mlir/IR/MLIRContext.h
@@ -67,7 +67,6 @@ class RemarkEngine;
class MLIRContext {
public:
enum class Threading { DISABLED, ENABLED };
-
/// Create a new Context.
explicit MLIRContext(Threading multithreading = Threading::ENABLED);
explicit MLIRContext(const DialectRegistry ®istry,
@@ -256,14 +255,14 @@ class MLIRContext {
/// (attributes, operations, types, etc.).
llvm::hash_code getRegistryHash();
- /// Setup optimization remarks for the context.
- /// This will enable the remark engine and set the streamer to be used for
- /// optimization remarks.
+ /// 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 `printAsEmitRemarks` is true, the remarks will be printed as
+ /// 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(
std::unique_ptr<remark::detail::MLIRRemarkStreamerBase> streamer,
const remark::RemarkCategories &cats, bool printAsEmitRemarks = false);
>From 0b43c0ce3704f71c566b228624586c66ea23750c Mon Sep 17 00:00:00 2001
From: Guray Ozen <gozen at nvidia.com>
Date: Tue, 12 Aug 2025 16:01:04 +0000
Subject: [PATCH 15/22] add test
---
mlir/unittests/IR/RemarkTest.cpp | 42 +++++++++++++++++++++++++++++++-
1 file changed, 41 insertions(+), 1 deletion(-)
diff --git a/mlir/unittests/IR/RemarkTest.cpp b/mlir/unittests/IR/RemarkTest.cpp
index 7b2bef9f62724..027d9c694097e 100644
--- a/mlir/unittests/IR/RemarkTest.cpp
+++ b/mlir/unittests/IR/RemarkTest.cpp
@@ -17,12 +17,13 @@
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/LogicalResult.h"
#include "llvm/Support/YAMLParser.h"
+#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <optional>
using namespace llvm;
using namespace mlir;
-
+using namespace testing;
namespace {
TEST(Remark, TestOutputOptimizationRemark) {
@@ -75,6 +76,45 @@ TEST(Remark, TestOutputOptimizationRemark) {
auto bufferOrErr = MemoryBuffer::getFile(yamlFile);
ASSERT_TRUE(static_cast<bool>(bufferOrErr)) << "Failed to open remarks file";
std::string content = bufferOrErr.get()->getBuffer().str();
+
+ EXPECT_THAT(content, HasSubstr("--- !Passed"));
+ EXPECT_THAT(content, HasSubstr("Pass: Vectorizer"));
+ EXPECT_THAT(content, HasSubstr("Name: myPass1"));
+ EXPECT_THAT(content, HasSubstr("Remark: vectorized loop"));
+ EXPECT_THAT(content, HasSubstr("tripCount: '128'"));
+
+ EXPECT_THAT(content, HasSubstr("--- !Analysis"));
+ EXPECT_THAT(content, HasSubstr("Pass: Register"));
+ EXPECT_THAT(content, HasSubstr("Remark: Kernel uses 168 registers"));
+
+ EXPECT_THAT(content, HasSubstr("--- !Missed"));
+ EXPECT_THAT(content, HasSubstr("Pass: Unroll"));
+ EXPECT_THAT(content, HasSubstr("Name: MyPass"));
+ EXPECT_THAT(content,
+ HasSubstr("Reason: not profitable at this size"));
+ EXPECT_THAT(content,
+ HasSubstr("Suggestion: 'increase unroll factor to >=4'"));
+
+ EXPECT_THAT(content, HasSubstr("--- !Failure"));
+ EXPECT_THAT(content, HasSubstr("Pass: Unroll"));
+ EXPECT_THAT(content, HasSubstr("Name: MyPass"));
+ EXPECT_THAT(content,
+ HasSubstr("Reason: failed due to unsupported pattern"));
+
+ // Also verify document order to avoid false positives.
+ size_t iPassed = content.find("--- !Passed");
+ size_t iAnalysis = content.find("--- !Analysis");
+ size_t iMissed = content.find("--- !Missed");
+ size_t iFailure = content.find("--- !Failure");
+
+ ASSERT_NE(iPassed, std::string::npos);
+ ASSERT_NE(iAnalysis, std::string::npos);
+ ASSERT_NE(iMissed, std::string::npos);
+ ASSERT_NE(iFailure, std::string::npos);
+
+ EXPECT_LT(iPassed, iAnalysis);
+ EXPECT_LT(iAnalysis, iMissed);
+ EXPECT_LT(iMissed, iFailure);
}
TEST(Remark, TestNoOutputOptimizationRemark) {
>From a58c20f7db83b54d2b3dd7d204e6c881d56624d0 Mon Sep 17 00:00:00 2001
From: Guray Ozen <gozen at nvidia.com>
Date: Tue, 12 Aug 2025 16:28:20 +0000
Subject: [PATCH 16/22] improve the doc
---
mlir/docs/Remarks.md | 224 ++++++++++++++++++++++---------------------
1 file changed, 117 insertions(+), 107 deletions(-)
diff --git a/mlir/docs/Remarks.md b/mlir/docs/Remarks.md
index 22731d25eb507..35fc2eadcd793 100644
--- a/mlir/docs/Remarks.md
+++ b/mlir/docs/Remarks.md
@@ -1,147 +1,118 @@
# Remark Infrastructure
-[TOC]
+Remarks are **structured, human- and machine-readable notes** emitted by the
+compiler to explain:
-Remarks are structured, human- and machine-readable notes emitted by compiler to
-explain what was transformed, what was missed, and why. The `RemarkEngine`
-collects finalized remarks during compilation and forwards them to a pluggable
-streamer. A default streamer integrates LLVM’s `llvm::remarks` so you can stream
-while a pass runs and serialize to disk (YAML or LLVM bitstream) for tooling.
+- What was transformed
+- What was missed
+- Why it happened
-**Key points**
+The **`RemarkEngine`** collects finalized remarks during compilation and sends
+them to a pluggable **streamer**. By default, MLIR integrates with LLVM’s
+[`llvm::remarks`](https://llvm.org/docs/Remarks.html), allowing you to:
-- **Opt-in**: Disabled by default; zero overhead unless enabled.
-- **Per-context**: Configured on `MLIRContext`.
-- **Formats**: LLVM’s Remark engine (YAML / Bitstream), or pluggable custom streamers.
-- **Kinds**: `Passed`, `Missed`, `Failure`, `Analysis`.
-- **API**: Lightweight streaming interface with `<<` (similar to diagnostics).
+- Stream remarks as passes run
+- Serialize them to **YAML** or **LLVM bitstream** for tooling
-## How it works
+______________________________________________________________________
-Remarks has two important classes:
+## Key Points
-- **`RemarkEngine`** (owned by `MLIRContext`): receives finalized
- `InFlightRemark`s, optionally mirrors them to the `DiagnosticEngine`, then
+- **Opt-in** – Disabled by default; zero overhead unless enabled.
+- **Per-context** – Configured on `MLIRContext`.
+- **Formats** – LLVM Remark engine (YAML / Bitstream) or custom streamers.
+- **Kinds** – `Passed`, `Missed`, `Failure`, `Analysis`.
+- **API** – Lightweight streaming interface using `<<` (like MLIR diagnostics).
+
+______________________________________________________________________
+
+## How It Works
+
+Two main components:
+
+- **`RemarkEngine`** (owned by `MLIRContext`): Receives finalized
+ `InFlightRemark`s, optionally mirrors them to the `DiagnosticEngine`, and
dispatches to the installed streamer.
-- **`MLIRRemarkStreamerBase`** (abstract): backend interface with a single hook
- `streamOptimizationRemark(const Remark &)`.
-**Default backend – `MLIRLLVMRemarkStreamer`** Adapts `mlir::Remark` to
-`llvm::remarks::detail::Remark` and writes YAML/bitstream via
-`llvm::remarks::detail::RemarkStreamer` to a `ToolOutputFile`.
+- **`MLIRRemarkStreamerBase`** (abstract): Backend interface with a single hook:
+
+ ```c++
+ virtual void streamOptimizationRemark(const Remark &remark) = 0;
+ ```
+
+**Default backend – `MLIRLLVMRemarkStreamer`** Adapts `mlir::Remark` to LLVM’s
+remark format and writes YAML/bitstream via `llvm::remarks::RemarkStreamer`.
-**Ownership**: `MLIRContext` → `RemarkEngine` → `MLIRRemarkStreamerBase`.
+**Ownership flow:** `MLIRContext` → `RemarkEngine` → `MLIRRemarkStreamerBase`
+
+______________________________________________________________________
## Categories
-There are four built-in categories; you can extend these if needed.
+MLIR provides four built-in remark categories (extendable if needed):
-### Passed
+#### 1. **Passed**
-A diagnostic indicating the optimization/transformation succeeded.
+Optimization/transformation succeeded.
```c++
[Passed] Vectorizer:myPass1 {Remark=vectorized loop, tripCount=128}
```
-### Missed
+#### 2. **Missed**
-A diagnostic indicating the optimization/transformation did not apply, ideally
-with actionable feedback.
+Optimization/transformation didn’t apply — ideally with actionable feedback.
```c++
[Missed] Unroll: {Reason=tripCount=4 < threshold=256, Suggestion=increase unroll to 128}
```
-### Failure
+#### 3. **Failure**
-A diagnostic indicating the optimization/transformation attempted but failed (or
-could not run).
+Optimization/transformation attempted but failed (or could not run).
```c++
[Failure] Unroll: {Reason=failed due to unsupported pattern}
```
-### Analysis
+#### 4. **Analysis**
-A diagnostic reporting analysis results.
+Neutral analysis results.
```c++
[Analysis] Register: {Remark=Kernel uses 168 registers}
[Analysis] Register: {Remark=Kernel uses 10kB local memory}
```
-## Enable Remarks via mlir::emitRemarks (No Streamer)
-
-Enable once per `MLIRContext` (e.g., where you build your pass pipeline or in
-your tool). If `printAsEmitRemarks` is true, each remark is also mirrored to the
-context’s `DiagnosticEngine` under the provided category labels—handy for
-interactive tools and tests.
-
-```c++
-mlir::remark::RemarkCategories cats{/*passed=*/categoryLoopunroll,
- /*missed=*/std::nullopt,
- /*analysis=*/std::nullopt,
- /*failed=*/categoryLoopunroll};
-
-context.enableOptimizationRemarks(/*streamer=*/nullptr,
- cats,
- /*printAsEmitRemarks=*/true);
-```
-
-## Enable Remarks with LLVMRemarkStreamer (YAML/Bitstream)
-
-If you want to persist remarks to a file in YAML or bitstream format, use
-`mlir::remark::LLVMRemarkStreamer` (helper shown below):
-
-You can read more information about
-[LLVM's Remark from here](https://llvm.org/docs/Remarks.html).
-
-```c++
-#include "mlir/Remark/RemarkStreamer.h"
-
-mlir::remark::RemarkCategories cats{/*passed=*/categoryLoopunroll,
- /*missed=*/std::nullopt,
- /*analysis=*/std::nullopt,
- /*failed=*/categoryLoopunroll};
-
-mlir::remark::enableOptimizationRemarksToFile(
- context, yamlFile, llvm::remarks::Format::YAML, cats);
-```
+______________________________________________________________________
-## Emitting remarks from a pass
+## Emitting Remarks
-The `reportOptimization*` functions return an in-flight remark object (like MLIR
-diagnostics). Append strings and key–value pairs with `<<`.
+The `remark::*` helpers return an **in-flight remark**. You append strings or
+key–value metrics with `<<`.
```c++
#include "mlir/IR/Remarks.h"
-using namespace mlir;
-
LogicalResult MyPass::runOnOperation() {
- Operation *op = getOperation();
- Location loc = op->getLoc();
-
- // PASS: something succeeded
- remark::passed(loc, categoryVectorizer, myPassname1)
- << "vectorized loop" << remark::metric("tripCount", 128);
+ Location loc = getOperation()->getLoc();
- // ANALYSIS: neutral insight
- remark::analysis(loc, categoryRegister, "") << "Kernel uses 168 registers";
+ // PASS
+ remark::passed(loc, categoryVectorizer, myPassname1)
+ << "vectorized loop"
+ << remark::metric("tripCount", 128);
- // MISSED: explain why + suggest a fix
- int target = 128;
- int tripBad = 4;
- int threshold = 256;
- remark::missed(loc, categoryUnroll)
- << remark::reason("tripCount={0} < threshold={1}", tripBad, threshold);
+ // ANALYSIS
+ remark::analysis(loc, categoryRegister, "")
+ << "Kernel uses 168 registers";
+ // MISSED (reason + suggestion)
+ int tripBad = 4, threshold = 256, target = 128;
remark::missed(loc, categoryUnroll)
<< remark::reason("tripCount={0} < threshold={1}", tripBad, threshold)
<< remark::suggest("increase unroll to {0}", target);
- // FAILURE: action attempted but failed
+ // FAILURE
remark::failed(loc, categoryUnroll)
<< remark::reason("failed due to unsupported pattern");
@@ -149,11 +120,36 @@ LogicalResult MyPass::runOnOperation() {
}
```
-### Output formats
+______________________________________________________________________
+
+### Metrics and Shortcuts
+
+- **`remark::metric(key, value)`** – Adds a structured key–value metric.
+- **`remark::reason(fmt, ...)`** – Shortcut for `metric("Reason", ...)`.
+- **`remark::suggest(fmt, ...)`** – Shortcut for `metric("Suggestion", ...)`.
+
+If you pass a plain string (e.g. `<< "vectorized loop"`), it’s equivalent to
+`metric("Remark", "vectorized loop")`.
+
+______________________________________________________________________
+
+## Enabling Remarks
+
+### 1. **With LLVMRemarkStreamer (YAML or Bitstream)**
+
+Persists remarks to a file in the chosen format.
+
+```c++
+mlir::remark::RemarkCategories cats{/*passed=*/categoryLoopunroll,
+ /*missed=*/std::nullopt,
+ /*analysis=*/std::nullopt,
+ /*failed=*/categoryLoopunroll};
-#### YAML
+mlir::remark::enableOptimizationRemarksToFile(
+ context, yamlFile, llvm::remarks::Format::YAML, cats);
+```
-Readable, easy to diff and grep.
+**YAML format** – human-readable, easy to diff:
```yaml
--- !Passed
@@ -162,33 +158,47 @@ name: vectorizer
function: myFunc
loc: myfile.mlir:12:3
args:
- - key: tripCount
- value: 128
-message: "vectorized loop with tripCount=128"
+ - Remark: vectorized loop
+ - tripCount: 128
```
-#### Bitstream
+**Bitstream format** – compact binary for large runs.
+
+______________________________________________________________________
+
+### 2. **With `mlir::emitRemarks` (No Streamer)**
+
+If the streamer isn't passed, the remarks are mirrored to the `DiagnosticEngine`
+using `mlir::emitRemarks`
+
+```c++
+mlir::remark::RemarkCategories cats{/*passed=*/categoryLoopunroll,
+ /*missed=*/std::nullopt,
+ /*analysis=*/std::nullopt,
+ /*failed=*/categoryLoopunroll};
+
+context.enableOptimizationRemarks(
+ /*streamer=*/nullptr, cats,
+ /*printAsEmitRemarks=*/true);
+```
-Compact binary format supported by LLVM’s remark tooling. Prefer this for large
-production runs or when existing infrastructure already consumes LLVM remarks.
+______________________________________________________________________
-## Enable Remarks with a Custom Streamer
+### 3. **With a Custom Streamer**
-`RemarkEngine` talks to `MLIRRemarkStreamerBase`. Implement your own streamer to
-consume remarks in any format you like:
+You can implement a custom streamer by inheriting `MLIRRemarkStreamerBase` to
+consume remarks in any format.
```c++
class MyStreamer : public MLIRRemarkStreamerBase {
public:
void streamOptimizationRemark(const Remark &remark) override {
- // Convert Remark to your format and write it out.
+ // Convert and write remark to your custom format
}
};
-// ...
auto myStreamer = std::make_unique<MyStreamer>();
-context.setupOptimizationRemarks(path,
- std::move(myStreamer),
+context.setupOptimizationRemarks(path, std::move(myStreamer),
/*printAsEmitRemarks=*/false,
- /*categories=*/cat);
+ /*categories=*/cats);
```
>From 77da6edd9b3d9760606bb104cfd947e32249a05d Mon Sep 17 00:00:00 2001
From: Guray Ozen <gozen at nvidia.com>
Date: Thu, 14 Aug 2025 09:43:22 +0000
Subject: [PATCH 17/22] address @joker-eph comments
---
mlir/include/mlir/IR/MLIRContext.h | 22 +++-------------------
mlir/include/mlir/IR/Remarks.h | 17 ++++++++++++++++-
mlir/lib/IR/MLIRContext.cpp | 16 ----------------
mlir/lib/IR/Remarks.cpp | 17 +++++++++++++++++
mlir/lib/Remark/RemarkStreamer.cpp | 5 +++--
mlir/unittests/IR/RemarkTest.cpp | 6 +++---
6 files changed, 42 insertions(+), 41 deletions(-)
diff --git a/mlir/include/mlir/IR/MLIRContext.h b/mlir/include/mlir/IR/MLIRContext.h
index 6c3aac418fa79..ef22dc6fcb3ee 100644
--- a/mlir/include/mlir/IR/MLIRContext.h
+++ b/mlir/include/mlir/IR/MLIRContext.h
@@ -34,11 +34,7 @@ class MLIRContextImpl;
class RegisteredOperationName;
class StorageUniquer;
class IRUnit;
-namespace remark {
-struct RemarkCategories;
-}
namespace remark::detail {
-class MLIRRemarkStreamerBase;
class RemarkEngine;
} // namespace remark::detail
@@ -222,6 +218,9 @@ class MLIRContext {
/// Returns the remark engine for this context.
remark::detail::RemarkEngine *getRemarkEngine();
+ /// Set the remark engine for this context.
+ void setRemarkEngine(std::unique_ptr<remark::detail::RemarkEngine> engine);
+
/// Returns the storage uniquer used for creating affine constructs.
StorageUniquer &getAffineUniquer();
@@ -255,18 +254,6 @@ class MLIRContext {
/// (attributes, operations, types, etc.).
llvm::hash_code getRegistryHash();
- /// 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 `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(
- std::unique_ptr<remark::detail::MLIRRemarkStreamerBase> streamer,
- const remark::RemarkCategories &cats, bool printAsEmitRemarks = false);
-
//===--------------------------------------------------------------------===//
// Action API
//===--------------------------------------------------------------------===//
@@ -303,9 +290,6 @@ class MLIRContext {
}
private:
- /// Set the remark engine for this context.
- void setRemarkEngine(std::unique_ptr<remark::detail::RemarkEngine> engine);
-
/// Return true if the given dialect is currently loading.
bool isDialectLoading(StringRef dialectNamespace);
diff --git a/mlir/include/mlir/IR/Remarks.h b/mlir/include/mlir/IR/Remarks.h
index 7ba7a190324e4..e80eff44abced 100644
--- a/mlir/include/mlir/IR/Remarks.h
+++ b/mlir/include/mlir/IR/Remarks.h
@@ -213,7 +213,9 @@ struct LazyTextBuild {
/// Similar to InFlightDiagnostic, but for remarks.
class InFlightRemark {
public:
- explicit InFlightRemark(Remark *diag) : remark(diag) {}
+ explicit InFlightRemark(std::unique_ptr<Remark> diag)
+ : remark(std::move(diag)) {}
+
InFlightRemark(RemarkEngine &eng, std::unique_ptr<Remark> diag)
: owner(&eng), remark(std::move(diag)) {}
@@ -429,6 +431,19 @@ inline detail::InFlightRemark analysis(Location loc, StringRef cat,
passName, cat);
}
+/// 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 `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,
+ const remark::RemarkCategories &cats, bool printAsEmitRemarks = false);
+
} // namespace mlir::remark
#endif // MLIR_IR_REMARKS_H
diff --git a/mlir/lib/IR/MLIRContext.cpp b/mlir/lib/IR/MLIRContext.cpp
index 0f68af77a598b..802ca74616998 100644
--- a/mlir/lib/IR/MLIRContext.cpp
+++ b/mlir/lib/IR/MLIRContext.cpp
@@ -32,7 +32,6 @@
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Debug.h"
-#include "llvm/Support/Error.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/Mutex.h"
#include "llvm/Support/RWMutex.h"
@@ -408,21 +407,6 @@ remark::detail::RemarkEngine *MLIRContext::getRemarkEngine() {
return getImpl().remarkEngine.get();
}
-LogicalResult MLIRContext::enableOptimizationRemarks(
- std::unique_ptr<remark::detail::MLIRRemarkStreamerBase> streamer,
- const remark::RemarkCategories &cats, bool printAsEmitRemarks) {
- auto engine =
- std::make_unique<remark::detail::RemarkEngine>(printAsEmitRemarks, cats);
-
- std::string errMsg;
- if (failed(engine->initialize(std::move(streamer), &errMsg))) {
- llvm::report_fatal_error(
- llvm::Twine("Failed to initialize remark engine. Error: ") + errMsg);
- }
- setRemarkEngine(std::move(engine));
- return success();
-}
-
//===----------------------------------------------------------------------===//
// Dialect and Operation Registration
//===----------------------------------------------------------------------===//
diff --git a/mlir/lib/IR/Remarks.cpp b/mlir/lib/IR/Remarks.cpp
index abc042d0e4cb3..cab57b66ab439 100644
--- a/mlir/lib/IR/Remarks.cpp
+++ b/mlir/lib/IR/Remarks.cpp
@@ -243,3 +243,20 @@ RemarkEngine::RemarkEngine(bool printAsEmitRemarks,
if (cats.failed)
failedFilter = llvm::Regex(cats.failed.value());
}
+
+llvm::LogicalResult mlir::remark::enableOptimizationRemarks(
+ MLIRContext &ctx,
+ std::unique_ptr<remark::detail::MLIRRemarkStreamerBase> streamer,
+ const remark::RemarkCategories &cats, bool printAsEmitRemarks) {
+ auto engine =
+ std::make_unique<remark::detail::RemarkEngine>(printAsEmitRemarks, cats);
+
+ std::string errMsg;
+ if (failed(engine->initialize(std::move(streamer), &errMsg))) {
+ llvm::report_fatal_error(
+ llvm::Twine("Failed to initialize remark engine. Error: ") + errMsg);
+ }
+ ctx.setRemarkEngine(std::move(engine));
+
+ return success();
+}
diff --git a/mlir/lib/Remark/RemarkStreamer.cpp b/mlir/lib/Remark/RemarkStreamer.cpp
index e862543b682fe..0fe058cf102b3 100644
--- a/mlir/lib/Remark/RemarkStreamer.cpp
+++ b/mlir/lib/Remark/RemarkStreamer.cpp
@@ -62,8 +62,9 @@ LogicalResult enableOptimizationRemarksToFile(MLIRContext &ctx, StringRef path,
detail::LLVMRemarkStreamer::createToFile(path, fmt);
if (failed(sOr))
return failure();
- return ctx.enableOptimizationRemarks(std::move(*sOr), cat,
- printAsEmitRemarks);
+
+ return remark::enableOptimizationRemarks(ctx, std::move(*sOr), cat,
+ printAsEmitRemarks);
}
} // namespace mlir::remark
diff --git a/mlir/unittests/IR/RemarkTest.cpp b/mlir/unittests/IR/RemarkTest.cpp
index 027d9c694097e..b04afeffef37a 100644
--- a/mlir/unittests/IR/RemarkTest.cpp
+++ b/mlir/unittests/IR/RemarkTest.cpp
@@ -174,7 +174,7 @@ TEST(Remark, TestOutputOptimizationRemarkDiagnostic) {
/*failed=*/categoryUnroll};
LogicalResult isEnabled =
- context.enableOptimizationRemarks(nullptr, cats, true);
+ remark::enableOptimizationRemarks(context, nullptr, cats, true);
ASSERT_TRUE(succeeded(isEnabled)) << "Failed to enable remark engine";
@@ -246,8 +246,8 @@ TEST(Remark, TestCustomOptimizationRemarkDiagnostic) {
/*analysis=*/std::nullopt,
/*failed=*/categoryLoopunroll};
- LogicalResult isEnabled = context.enableOptimizationRemarks(
- std::make_unique<MyCustomStreamer>(), cats, true);
+ LogicalResult isEnabled = remark::enableOptimizationRemarks(
+ context, std::make_unique<MyCustomStreamer>(), cats, true);
ASSERT_TRUE(succeeded(isEnabled)) << "Failed to enable remark engine";
// Remark 1: pass, category LoopUnroll
>From e11388645aa07e3a8f42f2ecffb9f8b0513c9414 Mon Sep 17 00:00:00 2001
From: Guray Ozen <gozen at nvidia.com>
Date: Thu, 14 Aug 2025 09:58:41 +0000
Subject: [PATCH 18/22] Improve document for Failure
---
mlir/docs/Remarks.md | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/mlir/docs/Remarks.md b/mlir/docs/Remarks.md
index 35fc2eadcd793..870b74dd2f7e4 100644
--- a/mlir/docs/Remarks.md
+++ b/mlir/docs/Remarks.md
@@ -69,10 +69,19 @@ Optimization/transformation didn’t apply — ideally with actionable feedback.
#### 3. **Failure**
-Optimization/transformation attempted but failed (or could not run).
+Optimization/transformation attempted but failed. This is slightly different
+from the `Missed` category.
+
+For example, the user specifies `-use-max-register=100` when invoking the
+compiler, but the attempt fails for some reason:
+
+```bash
+$ your-compiler -use-max-register=100 mycode.xyz
+```
```c++
-[Failure] Unroll: {Reason=failed due to unsupported pattern}
+[Failed] { Limiting to use-max-register=100 failed;
+ ; it now uses 104 registers for better performance ... }
```
#### 4. **Analysis**
>From 39d4fff50a1ae64d1d6feb255ebcf258d11590e8 Mon Sep 17 00:00:00 2001
From: Guray Ozen <gozen at nvidia.com>
Date: Thu, 14 Aug 2025 10:01:38 +0000
Subject: [PATCH 19/22] Set better API for
enableOptimizationRemarksWithLLVMStreamer
---
mlir/docs/Remarks.md | 2 +-
mlir/include/mlir/Remark/RemarkStreamer.h | 7 +++----
mlir/lib/Remark/RemarkStreamer.cpp | 7 +++----
mlir/unittests/IR/RemarkTest.cpp | 5 +++--
4 files changed, 10 insertions(+), 11 deletions(-)
diff --git a/mlir/docs/Remarks.md b/mlir/docs/Remarks.md
index 870b74dd2f7e4..2f1210ed36fa5 100644
--- a/mlir/docs/Remarks.md
+++ b/mlir/docs/Remarks.md
@@ -154,7 +154,7 @@ mlir::remark::RemarkCategories cats{/*passed=*/categoryLoopunroll,
/*analysis=*/std::nullopt,
/*failed=*/categoryLoopunroll};
-mlir::remark::enableOptimizationRemarksToFile(
+mlir::remark::enableOptimizationRemarksWithLLVMStreamer(
context, yamlFile, llvm::remarks::Format::YAML, cats);
```
diff --git a/mlir/include/mlir/Remark/RemarkStreamer.h b/mlir/include/mlir/Remark/RemarkStreamer.h
index 148629223b530..c98cfd4fc83c9 100644
--- a/mlir/include/mlir/Remark/RemarkStreamer.h
+++ b/mlir/include/mlir/Remark/RemarkStreamer.h
@@ -43,9 +43,8 @@ namespace mlir::remark {
/// The remark categories are used to filter the remarks that are emitted.
/// If the printAsEmitRemarks flag is set, remarks will also be printed using
/// mlir::emitRemarks.
-LogicalResult enableOptimizationRemarksToFile(MLIRContext &ctx, StringRef path,
- llvm::remarks::Format fmt,
- const RemarkCategories &cat,
- bool printAsEmitRemarks = false);
+LogicalResult enableOptimizationRemarksWithLLVMStreamer(
+ MLIRContext &ctx, StringRef filePath, llvm::remarks::Format fmt,
+ const RemarkCategories &cat, bool printAsEmitRemarks = false);
} // namespace mlir::remark
diff --git a/mlir/lib/Remark/RemarkStreamer.cpp b/mlir/lib/Remark/RemarkStreamer.cpp
index 0fe058cf102b3..ceeb2c5e21d4a 100644
--- a/mlir/lib/Remark/RemarkStreamer.cpp
+++ b/mlir/lib/Remark/RemarkStreamer.cpp
@@ -53,10 +53,9 @@ LLVMRemarkStreamer::~LLVMRemarkStreamer() {
} // namespace mlir::remark::detail
namespace mlir::remark {
-LogicalResult enableOptimizationRemarksToFile(MLIRContext &ctx, StringRef path,
- llvm::remarks::Format fmt,
- const RemarkCategories &cat,
- bool printAsEmitRemarks) {
+LogicalResult enableOptimizationRemarksWithLLVMStreamer(
+ MLIRContext &ctx, StringRef path, llvm::remarks::Format fmt,
+ const RemarkCategories &cat, bool printAsEmitRemarks) {
FailureOr<std::unique_ptr<detail::MLIRRemarkStreamerBase>> sOr =
detail::LLVMRemarkStreamer::createToFile(path, fmt);
diff --git a/mlir/unittests/IR/RemarkTest.cpp b/mlir/unittests/IR/RemarkTest.cpp
index b04afeffef37a..3b73d87f25ca9 100644
--- a/mlir/unittests/IR/RemarkTest.cpp
+++ b/mlir/unittests/IR/RemarkTest.cpp
@@ -51,8 +51,9 @@ TEST(Remark, TestOutputOptimizationRemark) {
/*analysis=*/categoryRegister,
/*failed=*/categoryUnroll};
- LogicalResult isEnabled = mlir::remark::enableOptimizationRemarksToFile(
- context, yamlFile, llvm::remarks::Format::YAML, cats);
+ LogicalResult isEnabled =
+ mlir::remark::enableOptimizationRemarksWithLLVMStreamer(
+ context, yamlFile, llvm::remarks::Format::YAML, cats);
ASSERT_TRUE(succeeded(isEnabled)) << "Failed to enable remark engine";
// PASS: something succeeded
>From c6970b9f08e7d58c123dbceeafa6ce3393ab9add Mon Sep 17 00:00:00 2001
From: Guray Ozen <gozen at nvidia.com>
Date: Thu, 14 Aug 2025 14:04:37 +0000
Subject: [PATCH 20/22] few fixes
---
mlir/docs/Remarks.md | 9 ++++-----
mlir/include/mlir/IR/Remarks.h | 3 +++
mlir/include/mlir/Remark/RemarkStreamer.h | 3 +--
3 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/mlir/docs/Remarks.md b/mlir/docs/Remarks.md
index 2f1210ed36fa5..b02b3825db850 100644
--- a/mlir/docs/Remarks.md
+++ b/mlir/docs/Remarks.md
@@ -185,8 +185,7 @@ mlir::remark::RemarkCategories cats{/*passed=*/categoryLoopunroll,
/*missed=*/std::nullopt,
/*analysis=*/std::nullopt,
/*failed=*/categoryLoopunroll};
-
-context.enableOptimizationRemarks(
+remark::enableOptimizationRemarks(
/*streamer=*/nullptr, cats,
/*printAsEmitRemarks=*/true);
```
@@ -207,7 +206,7 @@ public:
};
auto myStreamer = std::make_unique<MyStreamer>();
-context.setupOptimizationRemarks(path, std::move(myStreamer),
- /*printAsEmitRemarks=*/false,
- /*categories=*/cats);
+remark::enableOptimizationRemarks(
+ /*streamer=*/myStreamer, cats,
+ /*printAsEmitRemarks=*/true);
```
diff --git a/mlir/include/mlir/IR/Remarks.h b/mlir/include/mlir/IR/Remarks.h
index e80eff44abced..94ca5353755ed 100644
--- a/mlir/include/mlir/IR/Remarks.h
+++ b/mlir/include/mlir/IR/Remarks.h
@@ -25,6 +25,9 @@
#include "mlir/IR/Value.h"
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;
};
diff --git a/mlir/include/mlir/Remark/RemarkStreamer.h b/mlir/include/mlir/Remark/RemarkStreamer.h
index c98cfd4fc83c9..8bfd176d9bade 100644
--- a/mlir/include/mlir/Remark/RemarkStreamer.h
+++ b/mlir/include/mlir/Remark/RemarkStreamer.h
@@ -1,5 +1,4 @@
-//===- RemarkStreamer.h - MLIR Optimization Remark ----------------------*-
-// C++-*-===//
+//===- RemarkStreamer.h - MLIR Optimization Remark ---------------*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
>From 219308fd55ed70bbc5417f43185683a9ee23f419 Mon Sep 17 00:00:00 2001
From: Guray Ozen <gozen at nvidia.com>
Date: Thu, 14 Aug 2025 15:22:58 +0000
Subject: [PATCH 21/22] add LLVM_UNLIKELY
---
mlir/include/mlir/IR/Remarks.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mlir/include/mlir/IR/Remarks.h b/mlir/include/mlir/IR/Remarks.h
index 94ca5353755ed..fc518c4d69733 100644
--- a/mlir/include/mlir/IR/Remarks.h
+++ b/mlir/include/mlir/IR/Remarks.h
@@ -374,7 +374,7 @@ inline InFlightRemark withEngine(Fn fn, Location loc, Args &&...args) {
RemarkEngine *enginePtr = ctx->getRemarkEngine();
- if (enginePtr)
+ if (LLVM_UNLIKELY(enginePtr))
return (enginePtr->*fn)(loc, std::forward<Args>(args)...);
return {};
>From c3be28a05178eabb17fdc10d1accd59cd7286409 Mon Sep 17 00:00:00 2001
From: Guray Ozen <gozen at nvidia.com>
Date: Fri, 15 Aug 2025 13:03:54 +0000
Subject: [PATCH 22/22] address comments
---
mlir/docs/Remarks.md | 5 ++-
mlir/include/mlir/IR/Remarks.h | 57 +++++++++++++++++++++-----------
mlir/lib/IR/Remarks.cpp | 4 +--
mlir/unittests/IR/RemarkTest.cpp | 2 ++
4 files changed, 43 insertions(+), 25 deletions(-)
diff --git a/mlir/docs/Remarks.md b/mlir/docs/Remarks.md
index b02b3825db850..bbe72b45ff3be 100644
--- a/mlir/docs/Remarks.md
+++ b/mlir/docs/Remarks.md
@@ -64,7 +64,7 @@ Optimization/transformation succeeded.
Optimization/transformation didn’t apply — ideally with actionable feedback.
```c++
-[Missed] Unroll: {Reason=tripCount=4 < threshold=256, Suggestion=increase unroll to 128}
+[Missed] Unroll: {Reason=tripCount is 4 smaller than the threshold=256, Suggestion=Reduce threshold to 4}
```
#### 3. **Failure**
@@ -80,8 +80,7 @@ $ your-compiler -use-max-register=100 mycode.xyz
```
```c++
-[Failed] { Limiting to use-max-register=100 failed;
- ; it now uses 104 registers for better performance ... }
+[Failed] {Remark=Limiting to use-max-register=100 failed; it now uses 104 registers for better performance ... }
```
#### 4. **Analysis**
diff --git a/mlir/include/mlir/IR/Remarks.h b/mlir/include/mlir/IR/Remarks.h
index fc518c4d69733..db3ddfb7dd44c 100644
--- a/mlir/include/mlir/IR/Remarks.h
+++ b/mlir/include/mlir/IR/Remarks.h
@@ -25,20 +25,30 @@
#include "mlir/IR/Value.h"
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.
+/// 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;
};
-/// Defines different remark kinds that can be used to categorize remarks.
+/// Categories describe the outcome of an optimization, not the mechanics of
+/// emitting/serializing remarks.
enum class RemarkKind {
OptimizationRemarkUnknown = 0,
- OptimizationRemarkPassed, // Optimization remark that was passed.
- OptimizationRemarkMissed, // Optimization remark that was missed.
- OptimizationRemarkFailure, // Optimization failure remark.
- OptimizationRemarkAnalysis, // Analysis remark that does not indicate a pass.
+
+ /// An optimization was applied.
+ OptimizationRemarkPassed,
+
+ /// A profitable optimization opportunity was found but not applied.
+ OptimizationRemarkMissed,
+
+ /// The compiler attempted the optimization but failed (e.g., legality
+ /// checks, or better opportunites).
+ OptimizationRemarkFailure,
+
+ /// Informational context (e.g., analysis numbers) without a pass/fail
+ /// outcome.
+ OptimizationRemarkAnalysis,
};
} // namespace mlir::remark
@@ -284,7 +294,7 @@ class RemarkEngine {
/// reported.
std::optional<llvm::Regex> missFilter;
/// The category for passed optimization remarks.
- std::optional<llvm::Regex> passFilter;
+ std::optional<llvm::Regex> passedFilter;
/// The category for analysis remarks.
std::optional<llvm::Regex> analysisFilter;
/// The category for failed optimization remarks.
@@ -312,10 +322,10 @@ class RemarkEngine {
/// Return true if any type of remarks are enabled for this pass.
bool isAnyRemarkEnabled(StringRef categoryName) const {
- return (isMissedOptRemarkEnabled(categoryName) ||
- isPassedOptRemarkEnabled(categoryName) ||
- isFailedOptRemarkEnabled(categoryName) ||
- isAnalysisOptRemarkEnabled(categoryName));
+ return isMissedOptRemarkEnabled(categoryName) ||
+ isPassedOptRemarkEnabled(categoryName) ||
+ isFailedOptRemarkEnabled(categoryName) ||
+ isAnalysisOptRemarkEnabled(categoryName);
}
/// Emit a remark using the given maker function, which should return
@@ -384,16 +394,24 @@ inline InFlightRemark withEngine(Fn fn, Location loc, Args &&...args) {
namespace mlir::remark {
+/// Create a Reason with llvm::formatv formatting.
template <class... Ts>
inline detail::LazyTextBuild reason(const char *fmt, Ts &&...ts) {
return {"Reason", [=] { return llvm::formatv(fmt, ts...).str(); }};
}
+/// Create a Suggestion with llvm::formatv formatting.
template <class... Ts>
inline detail::LazyTextBuild suggest(const char *fmt, Ts &&...ts) {
return {"Suggestion", [=] { return llvm::formatv(fmt, ts...).str(); }};
}
+/// Create a Remark with llvm::formatv formatting.
+template <class... Ts>
+inline detail::LazyTextBuild add(const char *fmt, Ts &&...ts) {
+ return {"Remark", [=] { return llvm::formatv(fmt, ts...).str(); }};
+}
+
template <class V>
inline detail::LazyTextBuild metric(StringRef key, V &&v) {
using DV = std::decay_t<V>;
@@ -435,13 +453,12 @@ inline detail::InFlightRemark analysis(Location loc, StringRef cat,
}
/// 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 `printAsEmitRemarks` is true, the remarks will be printed as
-/// mlir::emitRemarks.
-/// 'streamer' must inherit from MLIRRemarkStreamerBase and will be used to
-/// stream the remarks.
+/// 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
+/// `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,
diff --git a/mlir/lib/IR/Remarks.cpp b/mlir/lib/IR/Remarks.cpp
index cab57b66ab439..78a33efbe31ca 100644
--- a/mlir/lib/IR/Remarks.cpp
+++ b/mlir/lib/IR/Remarks.cpp
@@ -165,7 +165,7 @@ bool RemarkEngine::isMissedOptRemarkEnabled(StringRef categoryName) const {
}
bool RemarkEngine::isPassedOptRemarkEnabled(StringRef categoryName) const {
- return passFilter && passFilter->match(categoryName);
+ return passedFilter && passedFilter->match(categoryName);
}
bool RemarkEngine::isAnalysisOptRemarkEnabled(StringRef categoryName) const {
@@ -235,7 +235,7 @@ RemarkEngine::RemarkEngine(bool printAsEmitRemarks,
const RemarkCategories &cats)
: printAsEmitRemarks(printAsEmitRemarks) {
if (cats.passed)
- passFilter = llvm::Regex(cats.passed.value());
+ passedFilter = llvm::Regex(cats.passed.value());
if (cats.missed)
missFilter = llvm::Regex(cats.missed.value());
if (cats.analysis)
diff --git a/mlir/unittests/IR/RemarkTest.cpp b/mlir/unittests/IR/RemarkTest.cpp
index 3b73d87f25ca9..698e9735f2c2f 100644
--- a/mlir/unittests/IR/RemarkTest.cpp
+++ b/mlir/unittests/IR/RemarkTest.cpp
@@ -203,6 +203,8 @@ TEST(Remark, TestOutputOptimizationRemarkDiagnostic) {
<< remark::reason("failed due to unsupported pattern");
}
// clang-format off
+ unsigned long expectedSize = 5;
+ ASSERT_EQ(seenMsg.size(), expectedSize);
EXPECT_EQ(seenMsg[0], "[Passed] Vectorizer:myPass1 {Remark=vectorized loop, tripCount=128}");
EXPECT_EQ(seenMsg[1], "[Analysis] Register: {Remark=Kernel uses 168 registers}");
EXPECT_EQ(seenMsg[2], "[Missed] Unroll: {Reason=tripCount=4 < threshold=256}");
More information about the Mlir-commits
mailing list