diff options
| author | Guray Ozen <guray.ozen@gmail.com> | 2025-08-21 16:02:31 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-08-21 16:02:31 +0200 |
| commit | 3d41197d6803183cdcd88894d568a29215fd2afb (patch) | |
| tree | ca4a100cecf98b7d3c3fcde0cd310e5bc17a14ed /mlir/docs | |
| parent | 5d4aa87ca5176a54b8395141df565255e3fde9ed (diff) | |
[MLIR] Introduce RemarkEngine + pluggable remark streaming (YAML/Bitstream) (#152474)
This PR implements structured, tooling-friendly optimization remarks
with zero cost unless enabled. It implements:
- `RemarkEngine` collects finalized remarks within `MLIRContext`.
- `MLIRRemarkStreamerBase` abstract class streams them to a backend.
- Backends: `MLIRLLVMRemarkStreamer` (bridges to llvm::remarks →
YAML/Bitstream) or your own custom streamer.
- Optional mirroring to DiagnosticEngine (printAsEmitRemarks +
categories).
- Off by default; no behavior change unless enabled. Thread-safe;
ordering best-effort.
## Overview
```
Passes (reportOptimization*)
│
▼
+-------------------+
| RemarkEngine | collects
+-------------------+
│ │
│ mirror │ stream
▼ ▼
emitRemark MLIRRemarkStreamerBase (abstract)
│
├── MLIRLLVMRemarkStreamer → llvm::remarks → YAML | Bitstream
└── CustomStreamer → your sink
```
## Enable Remark engine and Plug LLVM's Remark streamer
```
// Enable once per MLIRContext. This uses `MLIRLLVMRemarkStreamer`
mlir::remark::enableOptimizationRemarksToFile(
ctx, path, llvm::remarks::Format::YAML, cats);
```
## API to emit remark
```
// Emit from a pass
remark::passed(loc, categoryVectorizer, myPassname1)
<< "vectorized loop";
remark::missed(loc, categoryUnroll, "MyPass")
<< remark::reason("not profitable at this size") // Creates structured reason arg
<< remark::suggest("increase unroll factor to >=4"); // Creates structured suggestion arg
remark::passed(loc, categoryVectorizer, myPassname1)
<< "vectorized loop"
<< remark::metric("tripCount", 128); // Create structured metric on-the-fly
```
Diffstat (limited to 'mlir/docs')
| -rw-r--r-- | mlir/docs/Remarks.md | 259 |
1 files changed, 259 insertions, 0 deletions
diff --git a/mlir/docs/Remarks.md b/mlir/docs/Remarks.md new file mode 100644 index 000000000000..ee2ea855b8a6 --- /dev/null +++ b/mlir/docs/Remarks.md @@ -0,0 +1,259 @@ +# Remark Infrastructure + +Remarks are **structured, human- and machine-readable notes** emitted by the +compiler to explain: + +- What was transformed +- What was missed +- Why it happened + +The **`RemarkEngine`** collects finalized remarks during compilation and sends +them to a pluggable **streamer**. By default, MLIR integrates with LLVM’s +[`llvm::remarks`](https://llvm.org/docs/Remarks.html), allowing you to: + +- Stream remarks as passes run +- Serialize them to **YAML** or **LLVM bitstream** for tooling + +*** + +## Key Points + +- **Opt-in** – Disabled by default; zero overhead unless enabled. +- **Per-context** – Configured on `MLIRContext`. +- **Formats** – LLVM Remark engine (YAML / Bitstream) or custom streamers. +- **Kinds** – `Passed`, `Missed`, `Failure`, `Analysis`. +- **API** – Lightweight streaming interface using `<<` (like MLIR diagnostics). + +*** + +## How It Works + +Two main components: + +- **`RemarkEngine`** (owned by `MLIRContext`): Receives finalized + `InFlightRemark`s, optionally mirrors them to the `DiagnosticEngine`, and + dispatches to the installed streamer. + +- **`MLIRRemarkStreamerBase`** (abstract): Backend interface with a single hook: + + ```c++ + virtual void streamOptimizationRemark(const Remark &remark) = 0; + ``` + +**Default backend – `MLIRLLVMRemarkStreamer`** Adapts `mlir::Remark` to LLVM’s +remark format and writes YAML/bitstream via `llvm::remarks::RemarkStreamer`. + +**Ownership flow:** `MLIRContext` → `RemarkEngine` → `MLIRRemarkStreamerBase` + +*** + +## Categories + +MLIR provides four built-in remark categories (extendable if needed): + +#### 1. **Passed** + +Optimization/transformation succeeded. + +``` +[Passed] RemarkName | Category:Vectorizer:myPass1 | Function=foo | Remark="vectorized loop", tripCount=128 +``` + +#### 2. **Missed** + +Optimization/transformation didn’t apply — ideally with actionable feedback. + +``` +[Missed] | Category:Unroll | Function=foo | Reason="tripCount=4 < threshold=256", Suggestion="increase unroll to 128" +``` + +#### 3. **Failure** + +Optimization/transformation attempted but failed. This is slightly different +from the `Missed` category. + +For example, the user specifies `-use-max-register=100` when invoking the +compiler, but the attempt fails for some reason: + +```bash +$ your-compiler -use-max-register=100 mycode.xyz +``` + +``` +[Failed] Category:RegisterAllocator | Reason="Limiting to use-max-register=100 failed; it now uses 104 registers for better performance" +``` + +#### 4. **Analysis** + +Neutral analysis results. + +``` +[Analysis] Category:Register | Remark="Kernel uses 168 registers" +[Analysis] Category:Register | Remark="Kernel uses 10kB local memory" +``` + +*** + +## Emitting Remarks + +The `remark::*` helpers return an **in-flight remark**. +You append strings or key–value metrics using `<<`. + +### Remark Options + +When constructing a remark, you typically provide four fields that are `StringRef`: + +1. **Remark name** – identifiable name +2. **Category** – high-level classification +3. **Sub-category** – more fine-grained classification +4. **Function name** – the function where the remark originates + + +### Example + +```c++ +#include "mlir/IR/Remarks.h" + +LogicalResult MyPass::runOnOperation() { + Location loc = getOperation()->getLoc(); + + remark::RemarkOpts opts = remark::RemarkOpts::name(MyRemarkName1) + .category(categoryVectorizer) + .function(fName) + .subCategory(myPassname1); + + // PASSED + remark::passed(loc, opts) + << "vectorized loop" + << remark::metric("tripCount", 128); + + // ANALYSIS + remark::analysis(loc, opts) + << "Kernel uses 168 registers"; + + // MISSED (with reason + suggestion) + int tripBad = 4, threshold = 256, target = 128; + remark::missed(loc, opts) + << remark::reason("tripCount={0} < threshold={1}", tripBad, threshold) + << remark::suggest("increase unroll to {0}", target); + + // FAILURE + remark::failed(loc, opts) + << remark::reason("failed due to unsupported pattern"); + + return success(); +} +``` + +*** + +### Metrics and Shortcuts + +Helper functions accept +[LLVM format](https://llvm.org/docs/ProgrammersManual.html#formatting-strings-the-formatv-function) +style strings. This format builds lazily, so remarks are zero-cost when +disabled. + +#### Adding Remarks + +- **`remark::add(fmt, ...)`** – Shortcut for `metric("Remark", ...)`. + +#### Adding Reasons + +- **`remark::reason(fmt, ...)`** – Shortcut for `metric("Reason", ...)`. Used to + explain why a remark was missed or failed. + +#### Adding Suggestions + +- **`remark::suggest(fmt, ...)`** – Shortcut for `metric("Suggestion", ...)`. + Used to provide actionable feedback. + +#### Adding Custom Metrics + +- **`remark::metric(key, value)`** – Adds a structured key–value metric. + +Example: tracking `TripCount`. When exported to YAML, it appears under `args` +for machine readability: + +```cpp +remark::metric("TripCount", value) +``` + +#### String Metrics + +Passing a plain string (e.g. `<< "vectorized loop"`) is equivalent to: + +```cpp +metric("Remark", "vectorized loop") +``` + +*** + +## Enabling Remarks + +### 1. **With LLVMRemarkStreamer (YAML or Bitstream)** + +Persists remarks to a file in the chosen format. + +```c++ +mlir::remark::RemarkCategories cats{/*passed=*/categoryLoopunroll, + /*missed=*/std::nullopt, + /*analysis=*/std::nullopt, + /*failed=*/categoryLoopunroll}; + +mlir::remark::enableOptimizationRemarksWithLLVMStreamer( + context, yamlFile, llvm::remarks::Format::YAML, cats); +``` + +**YAML format** – human-readable, easy to diff: + +```yaml +--- !Passed +pass: Category:SubCategory +name: MyRemarkName1 +function: myFunc +loc: myfile.mlir:12:3 +args: + - Remark: vectorized loop + - tripCount: 128 +``` + +**Bitstream format** – compact binary for large runs. + +*** + +### 2. **With `mlir::emitRemarks` (No Streamer)** + +If the streamer isn't passed, the remarks are mirrored to the `DiagnosticEngine` +using `mlir::emitRemarks` + +```c++ +mlir::remark::RemarkCategories cats{/*passed=*/categoryLoopunroll, + /*missed=*/std::nullopt, + /*analysis=*/std::nullopt, + /*failed=*/categoryLoopunroll}; +remark::enableOptimizationRemarks( + /*streamer=*/nullptr, cats, + /*printAsEmitRemarks=*/true); +``` + +*** + +### 3. **With a Custom Streamer** + +You can implement a custom streamer by inheriting `MLIRRemarkStreamerBase` to +consume remarks in any format. + +```c++ +class MyStreamer : public MLIRRemarkStreamerBase { +public: + void streamOptimizationRemark(const Remark &remark) override { + // Convert and write remark to your custom format + } +}; + +auto myStreamer = std::make_unique<MyStreamer>(); +remark::enableOptimizationRemarks( + /*streamer=*/myStreamer, cats, + /*printAsEmitRemarks=*/true); +``` |
