CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
302 lines
7.8 KiB
Markdown
302 lines
7.8 KiB
Markdown
# 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<remark::RemarkEmittingPolicyFinal> policy =
|
||
std::make_unique<remark::RemarkEmittingPolicyFinal>();
|
||
|
||
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<remark::RemarkEmittingPolicyFinal> policy =
|
||
std::make_unique<remark::RemarkEmittingPolicyFinal>();
|
||
|
||
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<MyStreamer>();
|
||
remark::enableOptimizationRemarks(context, std::move(streamer), cats);
|
||
```
|