備考¶
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 function10 instructions in function
最適化備考の有効化¶
LLVM で最適化備考を有効にするには、備考診断とシリアライズされた備考の 2 つのモードがサポートされています。
備考診断¶
最適化備考は診断として出力できます。これらの診断は、必要に応じてフロントエンドに伝達されるか、llc や opt などのツールによって出力されます。
- -pass-remarks=<正規表現>¶
名前が指定された (POSIX) 正規表現に一致するパスからの最適化備考を有効にします。
- -pass-remarks-missed=<正規表現>¶
名前が指定された (POSIX) 正規表現に一致するパスからの失敗した最適化備考を有効にします。
- -pass-remarks-analysis=<正規表現>¶
名前が指定された (POSIX) 正規表現に一致するパスからの最適化分析備考を有効にします。
シリアライズされた備考¶
診断は開発中に役立ちますが、コンパイル後、特にパフォーマンス分析中に最適化備考を参照する方が便利なことがよくあります。
そのため、LLVM は各コンパイルユニットに対して生成された備考を、後で利用できるファイルにシリアライズできます。
デフォルトでは、シリアライズされた備考のフォーマットは YAML であり、オブジェクトファイルに セクション を付けて簡単に取得できます。
基本オプション
コンテンツ設定
備考をサポートするその他のツール
llvm-lto
gold-plugin と lld
シリアライズモード¶
備考をシリアライズするには、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> }
以下のエントリは必須です
<タイプ>
:成功
、失敗
、分析
、AnalysisFPCommute
、AnalysisAliasing
、Failure
を指定できます。<パス>
:この備考を出力したパスの名前。<名前>
:<パス>
からの備考の名前。<関数>
:関数の 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` の注釈インフラストラクチャの再利用。
コンパイル全体で作成された注釈エミッターに同じファイルと形式を使用する。
抽象化の追加レイヤーを犠牲にして。