# Remark Infrastructure Remarks are **structured, human- and machine-readable notes** emitted by the compiler to communicate: - What transformations were applied - What optimizations were missed - Why certain decisions were made The **`RemarkEngine`** collects remarks during compilation and routes them to a pluggable **streamer**. By default, MLIR integrates with LLVM's [`llvm::remarks`](https://llvm.org/docs/Remarks.html) infrastructure, enabling you to: - Stream remarks as passes run - Serialize to **YAML** or **LLVM Bitstream** *** ## Overview - **Opt-in** – Disabled by default; zero overhead unless enabled. - **Per-context** – Configured on `MLIRContext`. - **Formats** – LLVM Remark engine (YAML / Bitstream) or custom streamers. - **Kinds** – `Passed`, `Missed`, `Failure`, `Analysis`. - **API** – Lightweight streaming interface using `<<` (like MLIR diagnostics). *** ## Architecture The remark system consists of two main components: ### RemarkEngine Owned by `MLIRContext`, the engine: - Receives finalized `InFlightRemark` objects - Optionally mirrors remarks to the `DiagnosticEngine` - Dispatches to the installed streamer ### MLIRRemarkStreamerBase An abstract backend interface with a single hook: ```c++ virtual void streamOptimizationRemark(const Remark &remark) = 0; ``` The default implementation, **`MLIRLLVMRemarkStreamer`**, adapts `mlir::Remark` to LLVM's remark format and writes YAML or Bitstream via `llvm::remarks::RemarkStreamer`. **Ownership chain:** `MLIRContext` → `RemarkEngine` → `MLIRRemarkStreamerBase` *** ## Remark Categories MLIR provides four built-in categories: ### Passed An optimization or transformation succeeded. ``` [Passed] RemarkName | Category:Vectorizer:myPass1 | Function=foo | Remark="vectorized loop", tripCount=128 ``` ### Missed An optimization didn't apply and produces ideally an actionable feedback. ``` [Missed] | Category:Unroll | Function=foo | Reason="tripCount=4 < threshold=256", Suggestion="increase unroll to 128" ``` ### Failure An optimization was attempted but failed. Unlike `Missed`, this indicates an active attempt that couldn't complete. For example, when a user requests `--use-max-register=100` but the allocator cannot satisfy the constraint: ``` [Failed] Category:RegisterAllocator | Reason="Limiting to use-max-register=100 failed; it now uses 104 registers for better performance" ``` ### Analysis Neutral informational output—useful for profiling and debugging. ``` [Analysis] Category:Register | Remark="Kernel uses 168 registers" [Analysis] Category:Register | Remark="Kernel uses 10kB local memory" ``` *** ## Emitting Remarks Use the `remark::*` helpers to create an **in-flight remark**, then append content with the `<<` operator. ### Configuring Remark Options Each remark accepts four fields (all `StringRef`): | Field | Description | |***************-|************************************************| | **Name** | Identifiable name for the remark | | **Category** | High-level classification | | **Sub-category** | Fine-grained classification | | **Function** | The function where the remark originates | ### Basic Example ```c++ #include "mlir/IR/Remarks.h" LogicalResult MyPass::runOnOperation() { Location loc = getOperation()->getLoc(); auto opts = remark::RemarkOpts::name("VectorizeLoop") .category("Vectorizer") .subCategory("MyPass") .function("foo"); // Passed: transformation succeeded remark::passed(loc, opts) << "vectorized loop" << remark::metric("tripCount", 128); // Analysis: informational output remark::analysis(loc, opts) << "Kernel uses 168 registers"; // Missed: optimization skipped (with reason and suggestion) remark::missed(loc, opts) << remark::reason("tripCount={0} < threshold={1}", 4, 256) << remark::suggest("increase unroll factor to {0}", 128); // Failure: optimization attempted but failed remark::failed(loc, opts) << remark::reason("unsupported pattern encountered"); return success(); } ``` *** ## Metrics and Helpers All helper functions accept [LLVM format strings](https://llvm.org/docs/ProgrammersManual.html#formatting-strings-the-formatv-function), which build lazily—ensuring zero cost when remarks are disabled. | Helper | Description | |******************************--|******************************************| | `remark::metric(key, value)` | Adds a structured key–value pair | | `remark::add(fmt, ...)` | Shortcut for `metric("Remark", ...)` | | `remark::reason(fmt, ...)` | Shortcut for `metric("Reason", ...)` | | `remark::suggest(fmt, ...)` | Shortcut for `metric("Suggestion", ...)` | ### String Shorthand Appending a plain string: ```c++ remark::passed(loc, opts) << "vectorized loop"; ``` is equivalent to: ```c++ remark::passed(loc, opts) << remark::metric("Remark", "vectorized loop"); ``` ### Custom Metrics Add structured data for machine readability: ```c++ remark::passed(loc, opts) << "loop optimized" << remark::metric("TripCount", 128) << remark::metric("VectorWidth", 4); ``` *** ## Emitting Policies The `RemarkEngine` supports pluggable policies that control which remarks are emitted. ### RemarkEmittingPolicyAll Emits **all** remarks unconditionally. ### RemarkEmittingPolicyFinal Emits only the **final** remark for each location. This is useful in multi-pass compilers where an early pass may report a failure, but a later pass succeeds. **Example:** Only the successful remark is emitted: ```c++ auto opts = remark::RemarkOpts::name("Unroller").category("LoopUnroll"); // First pass: reports failure remark::failed(loc, opts) << "Loop could not be unrolled"; // Later pass: reports success (this is the one emitted) remark::passed(loc, opts) << "Loop unrolled successfully"; ``` You can also implement custom policies by inheriting from the policy interface. *** ## Enabling Remarks ### Option 1: LLVM Remark Streamer (YAML or Bitstream) Persist remarks to a file for post-processing: ```c++ // Setup categories remark::RemarkCategories cats{ /*passed=*/ "LoopUnroll", /*missed=*/ std::nullopt, /*analysis=*/ std::nullopt, /*failed=*/ "LoopUnroll" }; // Use final policy std::unique_ptr policy = std::make_unique(); remark::enableOptimizationRemarksWithLLVMStreamer( context, outputFile, llvm::remarks::Format::YAML, std::move(policy), cats); ``` **YAML output** (human-readable): ```yaml *** !Passed pass: Vectorizer:MyPass name: VectorizeLoop function: foo loc: input.mlir:12:3 args: - Remark: vectorized loop - tripCount: 128 ``` **Bitstream format** — compact binary for large-scale analysis. ### Option 2: Diagnostic Engine (No Streamer) Mirror remarks to the standard diagnostic output: ```c++ // Setup categories remark::RemarkCategories cats{ /*passed=*/ "LoopUnroll", /*missed=*/ std::nullopt, /*analysis=*/ std::nullopt, /*failed=*/ "LoopUnroll" }; // Use final policy std::unique_ptr policy = std::make_unique(); remark::enableOptimizationRemarks( context, /*streamer=*/ nullptr, /*policy=*/ std::move(policy), cats, /*printAsEmitRemarks=*/ true); ``` ### Option 3: Custom Streamer Implement your own backend for specialized output formats: ```c++ class MyStreamer : public MLIRRemarkStreamerBase { public: void streamOptimizationRemark(const Remark &remark) override { // Custom serialization logic } }; auto streamer = std::make_unique(); remark::enableOptimizationRemarks(context, std::move(streamer), cats); ```