[Mlir-commits] [mlir] [MLIR] Integrate LLVM Optimization Remarks Infrastructure (PR #152474)

Guray Ozen llvmlistbot at llvm.org
Mon Aug 11 04:49:51 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 1/5] [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 2/5] 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 3/5] 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 4/5] 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 5/5] 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 &registry,
@@ -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



More information about the Mlir-commits mailing list