備考

LLVM 備考診断の概要

LLVM は、パスから診断を出力し、最適化が実行されたかどうか、または特定の理由で失敗したかどうかを記述できます。これにより、ユーザーはコンパイルパイプライン中にコンパイラが何をしたかについて、より深く理解できます。

主な備考タイプは 3 つあります

成功

コンパイラによって正常に実行された最適化を記述する備考。

例:

foo inlined into bar with (cost=always): always inline attribute

失敗

コンパイラが実行しようとしたが実行できなかった最適化の試みを記述する備考。

例:

foo not inlined into bar because it should never be inlined
(cost=never): noinline function attribute

分析

分析結果を記述する備考。生成されたコードに関して、ユーザーにより多くの情報を提供できます。

例:

16 stack bytes in function
10 instructions in function

最適化備考の有効化

LLVM で最適化備考を有効にするには、備考診断とシリアライズされた備考の 2 つのモードがサポートされています。

備考診断

最適化備考は診断として出力できます。これらの診断は、必要に応じてフロントエンドに伝達されるか、llcopt などのツールによって出力されます。

-pass-remarks=<正規表現>

名前が指定された (POSIX) 正規表現に一致するパスからの最適化備考を有効にします。

-pass-remarks-missed=<正規表現>

名前が指定された (POSIX) 正規表現に一致するパスからの失敗した最適化備考を有効にします。

-pass-remarks-analysis=<正規表現>

名前が指定された (POSIX) 正規表現に一致するパスからの最適化分析備考を有効にします。

シリアライズされた備考

診断は開発中に役立ちますが、コンパイル後、特にパフォーマンス分析中に最適化備考を参照する方が便利なことがよくあります。

そのため、LLVM は各コンパイルユニットに対して生成された備考を、後で利用できるファイルにシリアライズできます。

デフォルトでは、シリアライズされた備考のフォーマットは YAML であり、オブジェクトファイルに セクション を付けて簡単に取得できます。

llcopt は以下のオプションをサポートしています

基本オプション

-pass-remarks-output=<ファイル名>

<ファイル名> で指定されたファイルへの備考のシリアライズを有効にします。

デフォルトでは、出力は YAML にシリアライズされます。

-pass-remarks-format=<フォーマット>

シリアライズされた備考の出力フォーマットを指定します。

サポートされているフォーマット

コンテンツ設定

-pass-remarks-filter=<正規表現>

名前が指定された (POSIX) 正規表現に一致するパスのみが最終出力にシリアライズされます。

-pass-remarks-with-hotness

PGO を使用して、最適化備考にプロファイルカウントを含めます。

-pass-remarks-hotness-threshold

最適化備考を出力するために必要な最小プロファイルカウント。

備考をサポートするその他のツール

llvm-lto

-lto-pass-remarks-output=<ファイル名>
-lto-pass-remarks-filter=<正規表現>
-lto-pass-remarks-format=<フォーマット>
-lto-pass-remarks-with-hotness
-lto-pass-remarks-hotness-threshold

gold-pluginlld

-opt-remarks-filename=<ファイル名>
-opt-remarks-filter=<正規表現>
-opt-remarks-format=<フォーマット>
-opt-remarks-with-hotness

シリアライズモード

備考をシリアライズするには、2 つのモードがあります

分離

このモードでは、備考とメタデータは個別にシリアライズされます。クライアントは最初にメタデータを解析し、次にメタデータを使用して備考を正しく解析する必要があります。

スタンドアロン

このモードでは、備考とメタデータは同じストリームにシリアライズされます。メタデータは常に備考の前にあります。

コンパイラはスタンドアロン備考の出力はサポートしていません。このモードは、プロジェクト全体の備考をマージできるリンカーなどの後処理ツールに適しています。

YAML 備考

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> }

以下のエントリは必須です

  • <タイプ>成功失敗分析AnalysisFPCommuteAnalysisAliasingFailure を指定できます。

  • <パス>:この備考を出力したパスの名前。

  • <名前><パス> からの備考の名前。

  • <関数>:関数の mangled 名。

DebugLoc エントリが指定されている場合、以下のフィールドが必要です

  • <ファイル>

  • <行>

  • <列>

arg エントリが指定されている場合、以下のフィールドが必要です

  • <キー>

  • <値>

DebugLoc エントリが arg エントリ内で指定されている場合、以下のフィールドが必要です

  • <arg-ファイル>

  • <arg-行>

  • <arg-列>

文字列テーブル付き YAML

YAML シリアライズは、yaml-strtab フォーマットを使用して文字列テーブルの使用をサポートしています。

このフォーマットは、YAML 出力の文字列を、メタデータを通じて個別に提供できる文字列テーブルのインデックスを表す整数に置き換えます。

以下のエントリは、YAML ルールに従いながら文字列テーブルを利用できます

  • <パス>

  • <名前>

  • <関数>

  • <ファイル>

  • <値>

  • <arg-ファイル>

現在、opt-viewer ディレクトリ 内のツールは、このフォーマットをサポートしていません。

YAML メタデータ

YAML フォーマットと共に使用されるメタデータは次のとおりです

  • マジックナンバー:"REMARKS\0"

  • バージョン番号:リトルエンディアン uint64_t

  • 文字列テーブルの合計サイズ (サイズ自体は除く):リトルエンディアン uint64_t

  • ヌル終端文字列のリスト

オプション

  • シリアライズされた備考診断への絶対ファイルパス:ヌル終端文字列。

メタデータが備考とは別にシリアライズされる場合、ファイルパスが存在し、備考がシリアライズされるファイルを指している必要があります。

メタデータが備考のヘッダーとしてのみ機能する場合、ファイルパスは省略できます。

LLVM ビットストリーム備考

このフォーマットは、LLVM ビットストリーム を使用して、備考とそれに関連付けられたメタデータをシリアライズします。

ビットストリーム備考ストリームは、先頭に配置されたマジックナンバー "RMRK" によって識別できます。

備考をシリアライズするためのフォーマットは、2 つの異なるブロックタイプで構成されています

META_BLOCK

ストリームの残りのコンテンツに関する情報を提供するブロック。

正確に 1 つのブロックが必要です。複数のメタデータブロックを持つことはエラーです。

このブロックには、以下のレコードを含めることができます

RECORD_META_CONTAINER_INFO

コンテナのバージョンとタイプ。

バージョン: u32

種類: u2

RECORD_META_REMARK_VERSION

注釈エントリのバージョン。これはコンテナのバージョンとは独立して変更される可能性があります。

バージョン: u32

RECORD_META_STRTAB

注釈エントリで使用される文字列テーブル。文字列テーブルの形式は、`\0` で区切られた文字列のシーケンスです。

RECORD_META_EXTERNAL_FILE

このメタデータに関連付けられた注釈ブロックを含む、外部注釈ファイルのパス。これは絶対パスです。

REMARK_BLOCK

注釈エントリを記述するブロック。

ファイルごとに0個以上のブロックが許可されます。各ブロックは、正しく解析されるために META_BLOCK に依存します。

このブロックには、以下のレコードを含めることができます

RECORD_REMARK_HEADER

注釈のヘッダー。これには、注釈に関するすべての必須情報が含まれています。

種類

u3

注釈名

VBR6 (文字列テーブルインデックス)

パス名

VBR6 (文字列テーブルインデックス)

関数名

VBR6 (文字列テーブルインデックス)

RECORD_REMARK_DEBUG_LOC

対応する注釈のソースの場所。このレコードはオプションです。

ファイル

VBR7 (文字列テーブルインデックス)

u32

u32

RECORD_REMARK_HOTNESS

注釈のホットネス。このレコードはオプションです。

ホットネス | VBR8 (文字列テーブルインデックス)

RECORD_REMARK_ARG_WITH_DEBUGLOC

デバッグ場所が関連付けられた注釈引数。

キー

VBR7 (文字列テーブルインデックス)

VBR7 (文字列テーブルインデックス)

ファイル

VBR7 (文字列テーブルインデックス)

u32

u32

RECORD_REMARK_ARG_WITHOUT_DEBUGLOC

デバッグ場所が関連付けられた注釈引数。

キー

VBR7 (文字列テーブルインデックス)

VBR7 (文字列テーブルインデックス)

注釈コンテナ

ビットストリーム注釈は、2つの異なるモードで使用できるように設計されています。

**分離モード**

分離モードは、通常コンパイル中に使用されるモードです。コンパイルの結果(通常はオブジェクトファイル)に出力されるメタデータをメモリに保持しながら、注釈エントリをストリームにシリアル化する方法を提供します。

**スタンドアロンモード**

スタンドアロンモードは、通常プログラムの配布後に保存および使用されます。外部依存関係なしにすべての注釈の解析を可能にするすべての情報が含まれています。

複数のモードをサポートするために、このフォーマットではビットストリーム注釈コンテナタイプの概念が導入されています。

**SeparateRemarksMeta:** 分離して出力されるメタデータ

このコンテナタイプは、以下のみを含む META_BLOCK のみが必要です。

通常、これはオブジェクトファイルのセクションに出力され、クライアントは中間製品から注釈とそれに関連付けられたメタデータを直接取得できます。

**SeparateRemarksFile:** 分離して出力される注釈エントリ

このコンテナタイプは、以下のみを含む META_BLOCK のみが必要です。

このコンテナタイプは、0個以上の REMARK_BLOCK を予期します。

通常、これはオブジェクトファイルとともにサイドファイルに出力され、コンパイラのメモリ消費量を増やすことなくストリーム配信できるように作られています。これは、SeparateRemarksMeta コンテナの RECORD_META_EXTERNAL_FILE エントリによって参照されます。

パーサーが分離注釈のメタデータを含むコンテナを解析しようとすると、バージョンとタイプを解析し、文字列テーブルをメモリに保持しながら外部ファイルを開き、そのメタデータを検証して注釈エントリを解析する必要があります。

適切にフォーマットされたファイルにするために、分離コンテナからのコンテナバージョンは一致する必要があります。

**スタンドアロン:** メタデータと注釈エントリが一緒に

このコンテナタイプは、以下のみを含む META_BLOCK のみが必要です。

このコンテナタイプは、0個以上の REMARK_BLOCK を予期します。

異なるコンテナタイプに対する**llvm-bcanalyzer**の完全な出力

SeparateRemarksMeta

<BLOCKINFO_BLOCK/>
<Meta BlockID=8 NumWords=13 BlockCodeSize=3>
  <Container info codeid=1 abbrevid=4 op0=5 op1=0/>
  <String table codeid=3 abbrevid=5/> blob data = 'pass\\x00key\\x00value\\x00'
  <External File codeid=4 abbrevid=6/> blob data = '/path/to/file/name'
</Meta>

SeparateRemarksFile

<BLOCKINFO_BLOCK/>
<Meta BlockID=8 NumWords=3 BlockCodeSize=3>
  <Container info codeid=1 abbrevid=4 op0=0 op1=1/>
  <Remark version codeid=2 abbrevid=5 op0=0/>
</Meta>
<Remark BlockID=9 NumWords=8 BlockCodeSize=4>
  <Remark header codeid=5 abbrevid=4 op0=2 op1=0 op2=1 op3=2/>
  <Remark debug location codeid=6 abbrevid=5 op0=3 op1=99 op2=55/>
  <Remark hotness codeid=7 abbrevid=6 op0=999999999/>
  <Argument with debug location codeid=8 abbrevid=7 op0=4 op1=5 op2=6 op3=11 op4=66/>
</Remark>

スタンドアロン

<BLOCKINFO_BLOCK/>
<Meta BlockID=8 NumWords=15 BlockCodeSize=3>
  <Container info codeid=1 abbrevid=4 op0=5 op1=2/>
  <Remark version codeid=2 abbrevid=5 op0=30/>
  <String table codeid=3 abbrevid=6/> blob data = 'pass\\x00remark\\x00function\\x00path\\x00key\\x00value\\x00argpath\\x00'
</Meta>
<Remark BlockID=9 NumWords=8 BlockCodeSize=4>
  <Remark header codeid=5 abbrevid=4 op0=2 op1=1 op2=0 op3=2/>
  <Remark debug location codeid=6 abbrevid=5 op0=3 op1=99 op2=55/>
  <Remark hotness codeid=7 abbrevid=6 op0=999999999/>
  <Argument with debug location codeid=8 abbrevid=7 op0=4 op1=5 op2=6 op3=11 op4=66/>
</Remark>

opt-viewer

`opt-viewer`ディレクトリには、シリアル化された注釈を視覚化および要約するツールの集合が含まれています。

これらのツールは、`yaml`形式のみをサポートしています。

opt-viewer.py

コンパイラとプログラムの相互作用に関する視覚的なフィードバックを提供するHTMLページを出力します。

例:

$ opt-viewer.py my_yaml_file.opt.yaml
$ opt-viewer.py my_build_dir/

opt-stats.py

入力セットの最適化注釈に関する統計を出力します。

例:

$ opt-stats.py my_yaml_file.opt.yaml

Total number of remarks           3


Top 10 remarks by pass:
  inline                         33%
  asm-printer                    33%
  prologepilog                   33%

Top 10 remarks:
  asm-printer/InstructionCount   33%
  inline/NoDefinition            33%
  prologepilog/StackSize         33%

opt-diff.py

2つのYAMLファイル間の最適化のすべての変更を含む新しいYAMLファイルを生成します。

通常、このツールは、以下の間の差分を実行するために使用されます。

  • 新しいコンパイラ + 修正済みソース vs 古いコンパイラ + 修正済みソース

  • 修正済みコンパイラ + 新しいソース vs 修正済みコンパイラ + 古いソース

この差分ファイルは、opt-viewer.py を使用して表示できます。

例:

$ 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

オブジェクトファイルへの注釈診断の出力

注釈診断のメタデータを含むセクションは、以下の形式で出力されます。

  • yaml-strtab

  • bitstream

これは、`-remarks-section=` フラグを使用してオーバーライドできます。

セクションの名前は次のとおりです。

  • `__LLVM,__remarks` (MachO)

C API

LLVMは、`libRemarks` という名前の共有ライブラリを通じて注釈を解析するために使用できるライブラリを提供します。

C API を介した一般的な使用法は次のとおりです。

LLVMRemarkParserRef Parser = LLVMRemarkParserCreateYAML(Buf, Size);
LLVMRemarkEntryRef Remark = NULL;
while ((Remark = LLVMRemarkParserGetNext(Parser))) {
   // use Remark
   LLVMRemarkEntryDispose(Remark); // Release memory.
}
bool HasError = LLVMRemarkParserHasError(Parser);
LLVMRemarkParserDispose(Parser);

注釈ストリーマー

`RemarkStreamer` インターフェースは、注釈を生成できるすべてのコンポーネントにわたって注釈のシリアル化機能を統合するために使用されます。

すべての注釈シリアル化は、`LLVMContext` に設定されたメインの注釈ストリーマーである `llvm::remarks::RemarkStreamer` を介して行われる必要があります。インターフェースは、`llvm::remarks::Remark` に変換された注釈オブジェクトを受け取り、要求されたタイプのメタデータなどを使用して、要求された形式にシリアル化します。

通常、特殊化された注釈ストリーマーは、`LLVMContext` に設定されたものへの参照を保持し、独自のタイプの診断で動作します。

たとえば、LLVM IRパスは、`llvm::remarks::Remark` オブジェクトに変換される `llvm::DiagnosticInfoOptimization*` を出力します。次に、clangは `clang::Diagnostic` オブジェクトを受け取る独自の特殊化された注釈ストリーマーを設定できます。これにより、フロントエンドのさまざまなコンポーネントがLLVM注釈と同じ手法を使用して注釈を出力できます。

これにより、以下の利点が得られます。

  • 構成: コンパイルパイプライン中に、複数のコンポーネントが、すべて同じメインストリーマーを介して注釈を出力する、特殊化された注釈ストリーマーを設定できます。

  • `lib/Remarks` の注釈インフラストラクチャの再利用。

  • コンパイル全体で作成された注釈エミッターに同じファイルと形式を使用する。

抽象化の追加レイヤーを犠牲にして。