| # Remarks | |
| ## Introduction to the LLVM remark diagnostics | |
| LLVM is able to emit diagnostics from passes describing whether an optimization | |
| has been performed or missed for a particular reason, which should give more | |
| insight to users about what the compiler did during the compilation pipeline. | |
| There are three main remark types: | |
| `Passed` | |
| : Remarks that describe a successful optimization performed by the compiler. | |
| **Example:** | |
| ``` | |
| foo inlined into bar with (cost=always): always inline attribute | |
| ``` | |
| `Missed` | |
| : Remarks that describe an attempt to an optimization by the compiler that | |
| could not be performed. | |
| **Example:** | |
| ``` | |
| foo not inlined into bar because it should never be inlined | |
| (cost=never): noinline function attribute | |
| ``` | |
| `Analysis` | |
| : Remarks that describe the result of an analysis, that can bring more | |
| information to the user regarding the generated code. | |
| **Example:** | |
| ``` | |
| 16 stack bytes in function | |
| ``` | |
| ``` | |
| 10 instructions in function | |
| ``` | |
| ## Enabling optimization remarks | |
| There are two modes that are supported for enabling optimization remarks in | |
| LLVM: through remark diagnostics, or through serialized remarks. | |
| See also the clang flags | |
| [-Rpass](https://clang.llvm.org/docs/UsersManual.html#options-to-emit-optimization-reports) | |
| and | |
| [-fsave-optimization-record](http://clang.llvm.org/docs/UsersManual.html#cmdoption-f-no-save-optimization-record). | |
| ### Remark diagnostics | |
| Optimization remarks can be emitted as diagnostics. These diagnostics will be | |
| propagated to front-ends if desired, or emitted by tools like {doc}`llc | |
| <CommandGuide/llc>` or {doc}`opt <CommandGuide/opt>`. | |
| :::{option} -pass-remarks=<regex> | |
| Enables optimization remarks from passes whose name match the given (POSIX) | |
| regular expression. | |
| ::: | |
| :::{option} -pass-remarks-missed=<regex> | |
| Enables missed optimization remarks from passes whose name match the given | |
| (POSIX) regular expression. | |
| ::: | |
| :::{option} -pass-remarks-analysis=<regex> | |
| Enables optimization analysis remarks from passes whose name match the given | |
| (POSIX) regular expression. | |
| ::: | |
| ### Serialized remarks | |
| While diagnostics are useful during development, it is often more useful to | |
| refer to optimization remarks post-compilation, typically during performance | |
| analysis. | |
| For that, LLVM can serialize the remarks produced for each compilation unit to | |
| a file that can be consumed later. | |
| By default, the format of the serialized remarks is {ref}`YAML | |
| <yamlremarks>`, and it can be accompanied by a {ref}`section <remarkssection>` | |
| in the object files to easily retrieve it. | |
| {doc}`llc <CommandGuide/llc>` and {doc}`opt <CommandGuide/opt>` support the | |
| following options: | |
| `Basic options` | |
| :::{option} -pass-remarks-output=<filename> | |
| Enables the serialization of remarks to a file specified in `<filename>`. | |
| By default, the output is serialized to {ref}`YAML <yamlremarks>`. | |
| ::: | |
| :::{option} -pass-remarks-format=<format> | |
| Specifies the output format of the serialized remarks. | |
| Supported formats: | |
| - {ref}`yaml <yamlremarks>` (default) | |
| - {ref}`bitstream <bitstreamremarks>` | |
| ::: | |
| `Content configuration` | |
| :::{option} -pass-remarks-filter=<regex> | |
| Only passes whose name match the given (POSIX) regular expression will be | |
| serialized to the final output. | |
| ::: | |
| :::{option} -pass-remarks-with-hotness | |
| With PGO, include profile count in optimization remarks. | |
| ::: | |
| :::{option} -pass-remarks-hotness-threshold | |
| The minimum profile count required for an optimization remark to be | |
| emitted. | |
| ::: | |
| Other tools that support remarks: | |
| {program}`llvm-lto` | |
| :::{option} -lto-pass-remarks-output=<filename> | |
| ::: | |
| :::{option} -lto-pass-remarks-filter=<regex> | |
| ::: | |
| :::{option} -lto-pass-remarks-format=<format> | |
| ::: | |
| :::{option} -lto-pass-remarks-with-hotness | |
| ::: | |
| :::{option} -lto-pass-remarks-hotness-threshold | |
| ::: | |
| {program}`gold-plugin` and {program}`lld` | |
| :::{option} -opt-remarks-filename=<filename> | |
| ::: | |
| :::{option} -opt-remarks-filter=<regex> | |
| ::: | |
| :::{option} -opt-remarks-format=<format> | |
| ::: | |
| :::{option} -opt-remarks-with-hotness | |
| ::: | |
| (yamlremarks)= | |
| ## YAML remarks | |
| A typical remark serialized to YAML looks like this: | |
| ```yaml | |
| --- !<TYPE> | |
| Pass: <pass> | |
| Name: <name> | |
| DebugLoc: { File: <file>, Line: <line>, Column: <column> } | |
| Function: <function> | |
| Hotness: <hotness> | |
| Args: | |
| - <key>: <value> | |
| DebugLoc: { File: <arg-file>, Line: <arg-line>, Column: <arg-column> } | |
| ``` | |
| The following entries are mandatory: | |
| - `<TYPE>`: can be `Passed`, `Missed`, `Analysis`, | |
| `AnalysisFPCommute`, `AnalysisAliasing`, `Failure`. | |
| - `<pass>`: the name of the pass that emitted this remark. | |
| - `<name>`: the name of the remark coming from `<pass>`. | |
| - `<function>`: the mangled name of the function. | |
| If a `DebugLoc` entry is specified, the following fields are required: | |
| - `<file>` | |
| - `<line>` | |
| - `<column>` | |
| If an `arg` entry is specified, the following fields are required: | |
| - `<key>` | |
| - `<value>` | |
| If a `DebugLoc` entry is specified within an `arg` entry, the following | |
| fields are required: | |
| - `<arg-file>` | |
| - `<arg-line>` | |
| - `<arg-column>` | |
| (optviewer)= | |
| ### YAML metadata | |
| The metadata used together with the YAML format is: | |
| - a magic number: "REMARKS\\0" | |
| - the version number: a little-endian uint64_t | |
| - 8 zero bytes. This space was previously used to encode the size of a string | |
| table. String table support for YAML remarks has been removed, use the | |
| bitstream format instead. | |
| Optional: | |
| - the absolute file path to the serialized remark diagnostics: a | |
| null-terminated string. | |
| When the metadata is serialized separately from the remarks, the file path | |
| should be present and point to the file where the remarks are serialized to. | |
| In case the metadata only acts as a header to the remarks, the file path can be | |
| omitted. | |
| (bitstreamremarks)= | |
| ## LLVM bitstream remarks | |
| This format is using {doc}`LLVM bitstream <BitCodeFormat>` to serialize remarks | |
| and their associated metadata. | |
| A bitstream remark stream can be identified by the magic number `"RMRK"` that | |
| is placed at the very beginning. | |
| The format for serializing remarks is composed of two different block types: | |
| (bitstreamremarksmetablock)= | |
| ### META_BLOCK | |
| The block providing information about the rest of the content in the stream. | |
| Exactly one block is expected. Having multiple metadata blocks is an error. | |
| This block can contain the following records: | |
| (bitstreamremarksrecordmetacontainerinfo)= | |
| `RECORD_META_CONTAINER_INFO` | |
| The container version and type. | |
| Version: u32 | |
| Type: u2 | |
| (bitstreamremarksrecordmetaremarkversion)= | |
| `RECORD_META_REMARK_VERSION` | |
| The version of the remark entries. This can change independently from the | |
| container version. | |
| Version: u32 | |
| (bitstreamremarksrecordmetastrtab)= | |
| `RECORD_META_STRTAB` | |
| The string table used by the remark entries. The format of the string table | |
| is a sequence of strings separated by `\0`. | |
| (bitstreamremarksrecordmetaexternalfile)= | |
| `RECORD_META_EXTERNAL_FILE` | |
| The external remark file path that contains the remark blocks associated | |
| with this metadata. This is an absolute path. | |
| (bitstreamremarksremarkblock)= | |
| ### REMARK_BLOCK | |
| The block describing a remark entry. | |
| 0 or more blocks per file are allowed. Each block will depend on the | |
| {ref}`META_BLOCK <bitstreamremarksmetablock>` in order to be parsed correctly. | |
| This block can contain the following records: | |
| `RECORD_REMARK_HEADER` | |
| The header of the remark. This contains all the mandatory information about | |
| a remark. | |
| ```{list-table} | |
| * - Type | |
| - u3 | |
| * - Remark name | |
| - VBR6 (string table index) | |
| * - Pass name | |
| - VBR6 (string table index) | |
| * - Function name | |
| - VBR6 (string table index) | |
| ``` | |
| `RECORD_REMARK_DEBUG_LOC` | |
| The source location for the corresponding remark. This record is optional. | |
| ```{list-table} | |
| * - File | |
| - VBR7 (string table index) | |
| * - Line | |
| - u32 | |
| * - Column | |
| - u32 | |
| ``` | |
| `RECORD_REMARK_HOTNESS` | |
| The hotness of the remark. This record is optional. | |
| ```{list-table} | |
| * - Hotness | |
| - VBR8 (string table index) | |
| ``` | |
| `RECORD_REMARK_ARG_WITH_DEBUGLOC` | |
| A remark argument with an associated debug location. | |
| ```{list-table} | |
| * - Key | |
| - VBR7 (string table index) | |
| * - Value | |
| - VBR7 (string table index) | |
| * - File | |
| - VBR7 (string table index) | |
| * - Line | |
| - u32 | |
| * - Column | |
| - u32 | |
| ``` | |
| `RECORD_REMARK_ARG_WITHOUT_DEBUGLOC` | |
| A remark argument with an associated debug location. | |
| ```{list-table} | |
| * - Key | |
| - VBR7 (string table index) | |
| * - Value | |
| - VBR7 (string table index) | |
| ``` | |
| ### The remark container | |
| The bitstream remark container supports multiple types: | |
| (bitstreamremarksfileexternal)= | |
| `RemarksFileExternal: a link to an external remarks file` | |
| This container type expects only a {ref}`META_BLOCK <bitstreamremarksmetablock>` containing only: | |
| - {ref}`RECORD_META_CONTAINER_INFO <bitstreamremarksrecordmetacontainerinfo>` | |
| - {ref}`RECORD_META_STRTAB <bitstreamremarksrecordmetastrtab>` | |
| - {ref}`RECORD_META_EXTERNAL_FILE <bitstreamremarksrecordmetaexternalfile>` | |
| Typically, this is emitted in a section in the object files, allowing | |
| clients to retrieve remarks and their associated metadata directly from | |
| intermediate products. | |
| The container versions of the external separate container should match in order to | |
| have a well-formed file. | |
| (bitstreamremarksfile)= | |
| `RemarksFile: a standalone remarks file` | |
| This container type expects a {ref}`META_BLOCK <bitstreamremarksmetablock>` containing only: | |
| - {ref}`RECORD_META_CONTAINER_INFO <bitstreamremarksrecordmetacontainerinfo>` | |
| - {ref}`RECORD_META_REMARK_VERSION <bitstreamremarksrecordmetaremarkversion>` | |
| Then, this container type expects 1 or more {ref}`REMARK_BLOCK <bitstreamremarksremarkblock>`. | |
| If no remarks are emitted, the meta blocks are also not emitted, so the file is empty. | |
| After the remark blocks, another {ref}`META_BLOCK <bitstreamremarksmetablock>` is emitted, containing: | |
| - {ref}`RECORD_META_STRTAB <bitstreamremarksrecordmetastrtab>` | |
| When the parser reads this container type, it jumps to the end of the file | |
| to read the string table before parsing the individual remarks. | |
| Standalone remarks files can be referenced by the | |
| {ref}`RECORD_META_EXTERNAL_FILE <bitstreamremarksrecordmetaexternalfile>` | |
| entry in the {ref}`RemarksFileExternal | |
| <bitstreamremarksfileexternal>` container. | |
| % FIXME: Add complete output of llvm-bcanalyzer on the different container types (once format changes are completed) | |
| ## opt-viewer | |
| The `opt-viewer` directory contains a collection of tools that visualize and | |
| summarize serialized remarks. | |
| The tools only support the `yaml` format. | |
| (optviewerpy)= | |
| ### opt-viewer.py | |
| Output a HTML page which gives visual feedback on compiler interactions with | |
| your program. | |
| **Examples:** | |
| ``` | |
| $ opt-viewer.py my_yaml_file.opt.yaml | |
| ``` | |
| ``` | |
| $ opt-viewer.py my_build_dir/ | |
| ``` | |
| ### opt-stats.py | |
| Output statistics about the optimization remarks in the input set. | |
| **Example:** | |
| ``` | |
| $ opt-stats.py my_yaml_file.opt.yaml | |
| Total number of remarks 3 | |
| Top 10 remarks by pass: | |
| inline 33% | |
| asm-printer 33% | |
| prolog-epilog 33% | |
| Top 10 remarks: | |
| asm-printer/InstructionCount 33% | |
| inline/NoDefinition 33% | |
| prolog-epilog/StackSize 33% | |
| ``` | |
| ### opt-diff.py | |
| Produce a new YAML file which contains all of the changes in optimizations | |
| between two YAML files. | |
| Typically, this tool should be used to do diffs between: | |
| - new compiler + fixed source vs old compiler + fixed source | |
| - fixed compiler + new source vs fixed compiler + old source | |
| This diff file can be displayed using {ref}`opt-viewer.py <optviewerpy>`. | |
| **Example:** | |
| ``` | |
| $ opt-diff.py my_opt_yaml1.opt.yaml my_opt_yaml2.opt.yaml -o my_opt_diff.opt.yaml | |
| $ opt-viewer.py my_opt_diff.opt.yaml | |
| ``` | |
| (remarkssection)= | |
| ## Emitting remark diagnostics in the object file | |
| A section containing metadata on remark diagnostics will be emitted for the | |
| following formats: | |
| - `bitstream` | |
| This can be overridden by using the flag `-remarks-section=<bool>`. | |
| The section is named: | |
| - `__LLVM,__remarks` (MachO) | |
| ## C API | |
| LLVM provides a library that can be used to parse remarks through a shared | |
| library named `libRemarks`. | |
| The typical usage through the C API is like the following: | |
| ```c | |
| LLVMRemarkParserRef Parser = LLVMRemarkParserCreateYAML(Buf, Size); | |
| LLVMRemarkEntryRef Remark = NULL; | |
| while ((Remark = LLVMRemarkParserGetNext(Parser))) { | |
| // use Remark | |
| LLVMRemarkEntryDispose(Remark); // Release memory. | |
| } | |
| bool HasError = LLVMRemarkParserHasError(Parser); | |
| LLVMRemarkParserDispose(Parser); | |
| ``` | |
| ## Remark streamers | |
| The `RemarkStreamer` interface is used to unify the serialization | |
| capabilities of remarks across all the components that can generate remarks. | |
| All remark serialization should go through the main remark streamer, the | |
| `llvm::remarks::RemarkStreamer` set up in the `LLVMContext`. The interface | |
| takes remark objects converted to `llvm::remarks::Remark`, and takes care of | |
| serializing it to the requested format, using the requested type of metadata, | |
| etc. | |
| Typically, a specialized remark streamer will hold a reference to the one set | |
| up in the `LLVMContext`, and will operate on its own type of diagnostics. | |
| For example, LLVM IR passes will emit `llvm::DiagnosticInfoOptimization*` | |
| that get converted to `llvm::remarks::Remark` objects. Then, clang could set | |
| up its own specialized remark streamer that takes `clang::Diagnostic` | |
| objects. This can allow various components of the frontend to emit remarks | |
| using the same techniques as the LLVM remarks. | |
| This gives us the following advantages: | |
| - Composition: during the compilation pipeline, multiple components can set up | |
| their specialized remark streamers that all emit remarks through the same | |
| main streamer. | |
| - Re-using the remark infrastructure in `lib/Remarks`. | |
| - Using the same file and format for the remark emitters created throughout the | |
| compilation. | |
| at the cost of an extra layer of abstraction. | |
| % FIXME: add documentation for llvm-opt-report. | |
| % FIXME: add documentation for Passes supporting optimization remarks | |
| % FIXME: add documentation for IR Passes | |
| % FIXME: add documentation for CodeGen Passes |